Merge branch 'master' into pr/2620
@@ -48,7 +48,7 @@ jobs:
|
|||||||
- image: cimg/node:16.11.0
|
- image: cimg/node:16.11.0
|
||||||
|
|
||||||
working_directory: ~/homebrewery
|
working_directory: ~/homebrewery
|
||||||
parallelism: 4
|
parallelism: 1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
@@ -61,15 +61,15 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Test - Basic
|
name: Test - Basic
|
||||||
command: npm run test:basic
|
command: npm run test:basic
|
||||||
- run:
|
|
||||||
name: Test - Coverage
|
|
||||||
command: npm run test:coverage
|
|
||||||
- run:
|
- run:
|
||||||
name: Test - Mustache Spans
|
name: Test - Mustache Spans
|
||||||
command: npm run test:mustache-span
|
command: npm run test:mustache-syntax
|
||||||
- run:
|
- run:
|
||||||
name: Test - Routes
|
name: Test - Routes
|
||||||
command: npm run test:route
|
command: npm run test:route
|
||||||
|
- run:
|
||||||
|
name: Test - Coverage
|
||||||
|
command: npm run test:coverage
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ module.exports = {
|
|||||||
'react/jsx-no-bind' : ['error', { allowArrowFunctions: true }],
|
'react/jsx-no-bind' : ['error', { allowArrowFunctions: true }],
|
||||||
'react/jsx-uses-react' : 'error',
|
'react/jsx-uses-react' : 'error',
|
||||||
'react/prefer-es6-class' : ['error', 'never'],
|
'react/prefer-es6-class' : ['error', 'never'],
|
||||||
|
'jest/valid-expect' : ['error', { maxArgs: 2 }],
|
||||||
|
|
||||||
/** Warnings **/
|
/** Warnings **/
|
||||||
'max-lines' : ['warn', {
|
'max-lines' : ['warn', {
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -12,3 +12,5 @@ todo.md
|
|||||||
startDB.bat
|
startDB.bat
|
||||||
startMViewer.bat
|
startMViewer.bat
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
coverage
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:16.11-alpine
|
FROM node:16.13-alpine
|
||||||
RUN apk --no-cache add git
|
RUN apk --no-cache add git
|
||||||
|
|
||||||
ENV NODE_ENV=docker
|
ENV NODE_ENV=docker
|
||||||
|
|||||||
32
README.md
@@ -21,24 +21,29 @@ below.
|
|||||||
First, install three programs that The Homebrewery requires to run and retrieve
|
First, install three programs that The Homebrewery requires to run and retrieve
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
1. install [node](https://nodejs.org/en/)
|
1. install [node](https://nodejs.org/en/), version v16 or higher.
|
||||||
1. install [mongodb](https://www.mongodb.com/try/download/community) (Community version)
|
1. install [mongodb](https://www.mongodb.com/try/download/community) (Community version)
|
||||||
|
|
||||||
For the easiest installation, follow these steps:
|
For the easiest installation, follow these steps:
|
||||||
1. In the installer, uncheck the option to run as a service.
|
1. In the installer, uncheck the option to run as a service.
|
||||||
1. You can install MongoDB Compass if you want a GUI to view your database documents.
|
1. You can install MongoDB Compass if you want a GUI to view your database documents.
|
||||||
|
1. If you install any version over 6.0, you will have to install [MongoDB Shell](https://www.mongodb.com/try/download/shell).
|
||||||
1. Go to the C:\ drive and create a folder called "data".
|
1. Go to the C:\ drive and create a folder called "data".
|
||||||
1. Inside the "data" folder, create a new folder called "db".
|
1. Inside the "data" folder, create a new folder called "db".
|
||||||
1. Open a command prompt or other terminal and navigate to your MongoDB install folder (C:\Program Files\Mongo\Server\4.4\bin).
|
1. Open a command prompt or other terminal and navigate to your MongoDB install folder (C:\Program Files\Mongo\Server\6.0\bin).
|
||||||
1. In the command prompt, run "mongod", which will start up your local database server.
|
1. In the command prompt, run "mongod", which will start up your local database server.
|
||||||
1. While MongoD is running, open a second command prompt and navigate to the MongoDB install folder.
|
1. While MongoD is running, open a second command prompt and navigate to the MongoDB install folder.
|
||||||
1. In the second command prompt, run "mongo", which allows you to edit the database.
|
|
||||||
1. Type `use homebrewery` to create The Homebrewery database. You should see `switched to db homebrewery`.
|
|
||||||
1. Type `db.brews.insert({"title":"test"})` to create a blank document. You should see `WriteResult({ "nInserted" : 1 })`.
|
|
||||||
1. Search in Windows for "Advanced system settings" and open it.
|
1. Search in Windows for "Advanced system settings" and open it.
|
||||||
1. Click "Environment variables", find the "path" variable, and double-click to open it.
|
1. Click "Environment variables", find the "path" variable, and double-click to open it.
|
||||||
1. Click "New" and paste in the path to the MongoDB "bin" folder.
|
1. Click "New" and paste in the path to the MongoDB "bin" folder.
|
||||||
1. Click "OK" three times to close all the windows.
|
1. Click "OK" three times to close all the windows.
|
||||||
|
1. In the second command prompt, run "mongo", which allows you to edit the database.
|
||||||
|
1. Type `use homebrewery` to create The Homebrewery database. You should see `switched to db homebrewery`.
|
||||||
|
1. Type `db.brews.insertOne({"title":"test"})` to create a blank document. You should see `{
|
||||||
|
acknowledged: true,
|
||||||
|
insertedId: ObjectId("63c2fce9e5ac5a94fe2410cf")
|
||||||
|
}`
|
||||||
|
|
||||||
1. install [git](https://git-scm.com/downloads) (select the option that allows Git to run from the command prompt).
|
1. install [git](https://git-scm.com/downloads) (select the option that allows Git to run from the command prompt).
|
||||||
|
|
||||||
Checkout the repo ([documentation][github-clone-repo-docs-url]):
|
Checkout the repo ([documentation][github-clone-repo-docs-url]):
|
||||||
@@ -51,11 +56,19 @@ git clone https://github.com/naturalcrit/homebrewery.git
|
|||||||
Second, you will need to add the environment variable `NODE_ENV=local` to allow
|
Second, you will need to add the environment variable `NODE_ENV=local` to allow
|
||||||
the project to run locally.
|
the project to run locally.
|
||||||
|
|
||||||
You can set this temporarily in your shell of choice:
|
You can set this **temporarily** (until you close the terminal) in your shell of choice with admin privileges:
|
||||||
* Windows Powershell: `$env:NODE_ENV="local"`
|
* Windows Powershell: `$env:NODE_ENV="local"`
|
||||||
* Windows CMD: `set NODE_ENV=local`
|
* Windows CMD: `set NODE_ENV=local`
|
||||||
* Linux / macOS: `export NODE_ENV=local`
|
* Linux / macOS: `export NODE_ENV=local`
|
||||||
|
|
||||||
|
If you want to add this variable **permanently** the steps are as follows:
|
||||||
|
1. Search in Windows for "Advanced system settings" and open it.
|
||||||
|
1. Click "Environment variables".
|
||||||
|
1. In System Variables, click "New"
|
||||||
|
1. Click "New" and write `NODE_ENV` as a name and `local` as the value.
|
||||||
|
1. Click "OK" three times to close all the windows.
|
||||||
|
This can be undone at any time if needed.
|
||||||
|
|
||||||
Third, you will need to install the Node dependencies, compile the app, and run
|
Third, you will need to install the Node dependencies, compile the app, and run
|
||||||
it using the two commands:
|
it using the two commands:
|
||||||
|
|
||||||
@@ -65,6 +78,13 @@ it using the two commands:
|
|||||||
You should now be able to go to [http://localhost:8000](http://localhost:8000)
|
You should now be able to go to [http://localhost:8000](http://localhost:8000)
|
||||||
in your browser and use The Homebrewery offline.
|
in your browser and use The Homebrewery offline.
|
||||||
|
|
||||||
|
If you had any issue at all, here are some links that may be useful:
|
||||||
|
- [Course](https://learn.mongodb.com/courses/m103-basic-cluster-administration) on cluster administration, useful for beginners
|
||||||
|
- [Mongo community forums](https://www.mongodb.com/community/forums/)
|
||||||
|
- Useful Stack Overflow links for your most probable errors: [1](https://stackoverflow.com/questions/44962540/mongod-and-mongo-commands-not-working-on-windows-10), [2](https://stackoverflow.com/questions/15053893/mongo-command-not-recognized-when-trying-to-connect-to-a-mongodb-server/41507803#41507803), [3](https://stackoverflow.com/questions/51224959/mongo-is-not-recognized-as-an-internal-or-external-command-operable-program-o)
|
||||||
|
|
||||||
|
If you still have problems, post in [Our Subreddit](https://www.reddit.com/r/homebrewery/) and we will help you.
|
||||||
|
|
||||||
### Running the application via Docker
|
### Running the application via Docker
|
||||||
|
|
||||||
Please see the docs here: [README.DOCKER.md](./README.DOCKER.md)
|
Please see the docs here: [README.DOCKER.md](./README.DOCKER.md)
|
||||||
|
|||||||
100
changelog.md
@@ -1,4 +1,23 @@
|
|||||||
```css
|
```css
|
||||||
|
.beta {
|
||||||
|
color : white;
|
||||||
|
padding : 4px 6px;
|
||||||
|
line-height : 1em;
|
||||||
|
background : grey;
|
||||||
|
border-radius : 12px;
|
||||||
|
font-family : monospace;
|
||||||
|
font-size : 10px;
|
||||||
|
font-weight : 800;
|
||||||
|
margin-top : -5px;
|
||||||
|
margin-bottom : -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fac {
|
||||||
|
height: 1em;
|
||||||
|
line-height: 2em;
|
||||||
|
margin-bottom: -0.05cm
|
||||||
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
font-size: .35cm !important;
|
font-size: .35cm !important;
|
||||||
}
|
}
|
||||||
@@ -61,16 +80,93 @@ pre {
|
|||||||
## changelog
|
## changelog
|
||||||
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
||||||
|
|
||||||
### V3.6.1
|
### XXXXday DD/MM/2023 - v3.8.0
|
||||||
{{taskList
|
{{taskList
|
||||||
|
|
||||||
##### Jeddai
|
##### Jeddai
|
||||||
|
|
||||||
* [X] Add content negotiation to exclude image requests from our API calls
|
* [X] Add content negotiation to exclude image requests from our API calls
|
||||||
|
|
||||||
Fixes issue [#2595](https://github.com/naturalcrit/homebrewery/issues/2595)
|
Fixes issue [#2595](https://github.com/naturalcrit/homebrewery/issues/2595)
|
||||||
|
|
||||||
|
##### G-Ambatte
|
||||||
|
|
||||||
|
* [x] Update server build scripts to fix Admin page
|
||||||
|
|
||||||
|
Fixes issues [#2657](https://github.com/naturalcrit/homebrewery/issues/2657)
|
||||||
|
|
||||||
|
* [x] Fix internal links inside `<div>` blocks not automatically receiving the `target=_self` attribute
|
||||||
|
|
||||||
|
Fixes issues [#2680](https://github.com/naturalcrit/homebrewery/issues/2680)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
### Friday 23/01/2023 - v3.6.0
|
### Monday 13/03/2023 - v3.7.2
|
||||||
|
{{taskList
|
||||||
|
|
||||||
|
##### Calculuschild
|
||||||
|
|
||||||
|
* [x] Fix wide Monster Stat Blocks not spanning columns on Legacy
|
||||||
|
}}
|
||||||
|
|
||||||
|
### Thursday 09/03/2023 - v3.7.1
|
||||||
|
{{taskList
|
||||||
|
|
||||||
|
##### Lucastucious (new contributor!)
|
||||||
|
|
||||||
|
* [x] Changed `filter: drop-shadow` to `box-shadow` on text boxes, making text selectable in PDFs again.
|
||||||
|
|
||||||
|
Fixes issues [#1569](https://github.com/naturalcrit/homebrewery/issues/1569)
|
||||||
|
|
||||||
|
{{note
|
||||||
|
**NOTE:** If you create your PDF on a computer with an old version of Mac Preview (v10 or older) you may see shadows appear as solid gray.
|
||||||
|
}}
|
||||||
|
|
||||||
|
##### MichielDeMey
|
||||||
|
|
||||||
|
* [x] Updated the Google Drive icon
|
||||||
|
* [x] Backend fix to unit tests failing intermittently
|
||||||
|
|
||||||
|
##### Calculuschild
|
||||||
|
|
||||||
|
* [x] Fix PDF pixelation on CoverPage text outlines
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
### Tuesday 28/02/2023 - v3.7.0
|
||||||
|
{{taskList
|
||||||
|
|
||||||
|
{{note
|
||||||
|
**NOTE:** Some new snippets will now show a {{beta BETA}} tag. Feel free to use them, but be aware we may change how they work depending on your feedback.
|
||||||
|
}}
|
||||||
|
|
||||||
|
##### Calculuschild
|
||||||
|
|
||||||
|
* [x] New {{openSans **IMAGES → WATERCOLOR EDGE** {{fac,mask-edge}} }} and {{openSans **WATERCOLOR CORNER** {{fac,mask-corner}} }} snippets for V3, which adds a stylish watercolor texture to the edge of your images! (Thanks to /u/flamableconcrete on Reddit for providing these image masks!)
|
||||||
|
|
||||||
|
* [x] Fix site not displaying on iOS devices
|
||||||
|
|
||||||
|
##### 5e-Cleric
|
||||||
|
|
||||||
|
* [x] New {{openSans **PHB → COVER PAGE** {{fac,book-front-cover}} }} snippet for V3, which adds a stylish coverpage to your brew! (Thanks to /u/Kaiburr_Kath-Hound on Reddit for providing some of these resources!)
|
||||||
|
|
||||||
|
##### MichielDeMey (new contribuor!)
|
||||||
|
|
||||||
|
* [x] Fix typo in testing scripts
|
||||||
|
* [x] Fix "mug" image not using HTTPS
|
||||||
|
|
||||||
|
Fixes issues [#2687](https://github.com/naturalcrit/homebrewery/issues/2687)
|
||||||
|
}}
|
||||||
|
|
||||||
|
### Saturday 18/02/2023 - v3.6.1
|
||||||
|
{{taskList
|
||||||
|
##### G-Ambatte
|
||||||
|
|
||||||
|
* [x] Fix users not being removed from Authors list correctly
|
||||||
|
|
||||||
|
Fixes issues [#2674](https://github.com/naturalcrit/homebrewery/issues/2674)
|
||||||
|
}}
|
||||||
|
|
||||||
|
### Monday 23/01/2023 - v3.6.0
|
||||||
{{taskList
|
{{taskList
|
||||||
##### calculuschild
|
##### calculuschild
|
||||||
|
|
||||||
|
|||||||
129
client/components/combobox.jsx
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
const React = require('react');
|
||||||
|
const createClass = require('create-react-class');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const cx = require('classnames');
|
||||||
|
require('./combobox.less');
|
||||||
|
|
||||||
|
const Combobox = createClass({
|
||||||
|
displayName : 'Combobox',
|
||||||
|
getDefaultProps : function() {
|
||||||
|
return {
|
||||||
|
className : '',
|
||||||
|
trigger : 'hover',
|
||||||
|
default : '',
|
||||||
|
placeholder : '',
|
||||||
|
autoSuggest : {
|
||||||
|
clearAutoSuggestOnClick : true,
|
||||||
|
suggestMethod : 'includes',
|
||||||
|
filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getInitialState : function() {
|
||||||
|
return {
|
||||||
|
showDropdown : false,
|
||||||
|
value : '',
|
||||||
|
options : [...this.props.options],
|
||||||
|
inputFocused : false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
componentDidMount : function() {
|
||||||
|
if(this.props.trigger == 'click')
|
||||||
|
document.addEventListener('click', this.handleClickOutside);
|
||||||
|
this.setState({
|
||||||
|
value : this.props.default
|
||||||
|
});
|
||||||
|
},
|
||||||
|
componentWillUnmount : function() {
|
||||||
|
if(this.props.trigger == 'click')
|
||||||
|
document.removeEventListener('click', this.handleClickOutside);
|
||||||
|
},
|
||||||
|
handleClickOutside : function(e){
|
||||||
|
// Close dropdown when clicked outside
|
||||||
|
if(this.refs.dropdown && !this.refs.dropdown.contains(e.target)) {
|
||||||
|
this.handleDropdown(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDropdown : function(show){
|
||||||
|
this.setState({
|
||||||
|
showDropdown : show,
|
||||||
|
inputFocused : this.props.autoSuggest.clearAutoSuggestOnClick ? show : false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleInput : function(e){
|
||||||
|
e.persist();
|
||||||
|
this.setState({
|
||||||
|
value : e.target.value,
|
||||||
|
inputFocused : false
|
||||||
|
}, ()=>{
|
||||||
|
this.props.onEntry(e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSelect : function(e){
|
||||||
|
this.setState({
|
||||||
|
value : e.currentTarget.getAttribute('data-value')
|
||||||
|
}, ()=>{this.props.onSelect(this.state.value);});
|
||||||
|
;
|
||||||
|
},
|
||||||
|
renderTextInput : function(){
|
||||||
|
return (
|
||||||
|
<div className='dropdown-input item'
|
||||||
|
onMouseEnter={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(true);} : undefined}
|
||||||
|
onClick= {this.props.trigger == 'click' ? ()=>{this.handleDropdown(true);} : undefined}>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
onChange={(e)=>this.handleInput(e)}
|
||||||
|
value={this.state.value || ''}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
onBlur={(e)=>{
|
||||||
|
if(!e.target.checkValidity()){
|
||||||
|
this.setState({
|
||||||
|
value : this.props.default
|
||||||
|
}, ()=>this.props.onEntry(e));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
renderDropdown : function(dropdownChildren){
|
||||||
|
if(!this.state.showDropdown) return null;
|
||||||
|
if(this.props.autoSuggest && !this.state.inputFocused){
|
||||||
|
const suggestMethod = this.props.autoSuggest.suggestMethod;
|
||||||
|
const filterOn = _.isString(this.props.autoSuggest.filterOn) ? [this.props.autoSuggest.filterOn] : this.props.autoSuggest.filterOn;
|
||||||
|
const filteredArrays = filterOn.map((attr)=>{
|
||||||
|
const children = dropdownChildren.filter((item)=>{
|
||||||
|
if(suggestMethod === 'includes'){
|
||||||
|
return item.props[attr]?.toLowerCase().includes(this.state.value.toLowerCase());
|
||||||
|
} else if(suggestMethod === 'startsWith'){
|
||||||
|
return item.props[attr]?.toLowerCase().startsWith(this.state.value.toLowerCase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
});
|
||||||
|
dropdownChildren = _.uniq(filteredArrays.flat(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='dropdown-options'>
|
||||||
|
{dropdownChildren}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
render : function () {
|
||||||
|
const dropdownChildren = this.state.options.map((child, i)=>{
|
||||||
|
const clone = React.cloneElement(child, { onClick: (e)=>this.handleSelect(e) });
|
||||||
|
return clone;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className={`dropdown-container ${this.props.className}`}
|
||||||
|
ref='dropdown'
|
||||||
|
onMouseLeave={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(false);} : undefined}>
|
||||||
|
{this.renderTextInput()}
|
||||||
|
{this.renderDropdown(dropdownChildren)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Combobox;
|
||||||
50
client/components/combobox.less
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
.dropdown-container {
|
||||||
|
position:relative;
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.dropdown-options {
|
||||||
|
position:absolute;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 100;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid gray;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 14px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #949494;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 3px solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
position:relative;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: Open Sans;
|
||||||
|
padding: 5px;
|
||||||
|
cursor: default;
|
||||||
|
margin: 0 3px;
|
||||||
|
//border-bottom: 1px solid darkgray;
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(120%);
|
||||||
|
background-color: rgb(163, 163, 163);
|
||||||
|
}
|
||||||
|
.detail {
|
||||||
|
width:100%;
|
||||||
|
text-align: left;
|
||||||
|
color: rgb(124, 124, 124);
|
||||||
|
font-style:italic;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ const BrewRenderer = createClass({
|
|||||||
style : '',
|
style : '',
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
theme : '5ePHB',
|
theme : '5ePHB',
|
||||||
|
lang : '',
|
||||||
errors : []
|
errors : []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -134,7 +135,8 @@ const BrewRenderer = createClass({
|
|||||||
|
|
||||||
renderStyle : function() {
|
renderStyle : function() {
|
||||||
if(!this.props.style) return;
|
if(!this.props.style) return;
|
||||||
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.style} </style>` }} />;
|
//return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>@layer styleTab {\n${this.props.style}\n} </style>` }} />;
|
||||||
|
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>\n${this.props.style}\n</style>` }} />;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderPage : function(pageText, index){
|
renderPage : function(pageText, index){
|
||||||
@@ -189,7 +191,6 @@ const BrewRenderer = createClass({
|
|||||||
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
||||||
const themePath = this.props.theme ?? '5ePHB';
|
const themePath = this.props.theme ?? '5ePHB';
|
||||||
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{!this.state.isMounted
|
{!this.state.isMounted
|
||||||
@@ -222,7 +223,7 @@ const BrewRenderer = createClass({
|
|||||||
&&
|
&&
|
||||||
<>
|
<>
|
||||||
{this.renderStyle()}
|
{this.renderStyle()}
|
||||||
<div className='pages' ref='pages'>
|
<div className='pages' ref='pages' lang={`${this.props.lang || 'en'}`}>
|
||||||
{this.renderPages()}
|
{this.renderPages()}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const _ = require('lodash');
|
|||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const request = require('../../utils/request-middleware.js');
|
const request = require('../../utils/request-middleware.js');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
const Combobox = require('client/components/combobox.jsx');
|
||||||
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
||||||
|
|
||||||
const Themes = require('themes/themes.json');
|
const Themes = require('themes/themes.json');
|
||||||
@@ -35,9 +36,10 @@ const MetadataEditor = createClass({
|
|||||||
authors : [],
|
authors : [],
|
||||||
systems : [],
|
systems : [],
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
theme : '5ePHB'
|
theme : '5ePHB',
|
||||||
|
lang : 'en'
|
||||||
},
|
},
|
||||||
onChange : ()=>{},
|
onChange : ()=>{},
|
||||||
reportError : ()=>{}
|
reportError : ()=>{}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -76,6 +78,7 @@ const MetadataEditor = createClass({
|
|||||||
const errMessage = validationErr.map((err)=>{
|
const errMessage = validationErr.map((err)=>{
|
||||||
return `- ${err}`;
|
return `- ${err}`;
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
|
|
||||||
callIfExists(e.target, 'setCustomValidity', errMessage);
|
callIfExists(e.target, 'setCustomValidity', errMessage);
|
||||||
callIfExists(e.target, 'reportValidity');
|
callIfExists(e.target, 'reportValidity');
|
||||||
}
|
}
|
||||||
@@ -111,6 +114,11 @@ const MetadataEditor = createClass({
|
|||||||
this.props.onChange(this.props.metadata);
|
this.props.onChange(this.props.metadata);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleLanguage : function(languageCode){
|
||||||
|
this.props.metadata.lang = languageCode;
|
||||||
|
this.props.onChange(this.props.metadata);
|
||||||
|
},
|
||||||
|
|
||||||
handleDelete : function(){
|
handleDelete : function(){
|
||||||
if(this.props.metadata.authors && this.props.metadata.authors.length <= 1){
|
if(this.props.metadata.authors && this.props.metadata.authors.length <= 1){
|
||||||
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
|
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
|
||||||
@@ -189,6 +197,10 @@ const MetadataEditor = createClass({
|
|||||||
return <div className='item' key={''} onClick={()=>this.handleTheme(theme)} title={''}>
|
return <div className='item' key={''} onClick={()=>this.handleTheme(theme)} title={''}>
|
||||||
{`${theme.renderer} : ${theme.name}`}
|
{`${theme.renderer} : ${theme.name}`}
|
||||||
<img src={`/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`}/>
|
<img src={`/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`}/>
|
||||||
|
<div className='preview'>
|
||||||
|
<h6>{`${theme.name}`} preview</h6>
|
||||||
|
<img src={`/themes/${theme.renderer}/${theme.path}/dropdownPreview.png`}/>
|
||||||
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -198,14 +210,14 @@ const MetadataEditor = createClass({
|
|||||||
|
|
||||||
if(this.props.metadata.renderer == 'legacy') {
|
if(this.props.metadata.renderer == 'legacy') {
|
||||||
dropdown =
|
dropdown =
|
||||||
<Nav.dropdown className='disabled' trigger='disabled'>
|
<Nav.dropdown className='disabled value' trigger='disabled'>
|
||||||
<div>
|
<div>
|
||||||
{`Themes are not supported in the Legacy Renderer`} <i className='fas fa-caret-down'></i>
|
{`Themes are not supported in the Legacy Renderer`} <i className='fas fa-caret-down'></i>
|
||||||
</div>
|
</div>
|
||||||
</Nav.dropdown>;
|
</Nav.dropdown>;
|
||||||
} else {
|
} else {
|
||||||
dropdown =
|
dropdown =
|
||||||
<Nav.dropdown trigger='click'>
|
<Nav.dropdown className='value' trigger='click'>
|
||||||
<div>
|
<div>
|
||||||
{`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`} <i className='fas fa-caret-down'></i>
|
{`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`} <i className='fas fa-caret-down'></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -220,6 +232,47 @@ const MetadataEditor = createClass({
|
|||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderLanguageDropdown : function(){
|
||||||
|
const langCodes = ['en', 'de', 'de-ch', 'fr', 'ja', 'es', 'it', 'sv', 'ru', 'zh-Hans', 'zh-Hant'];
|
||||||
|
const listLanguages = ()=>{
|
||||||
|
return _.map(langCodes.sort(), (code, index)=>{
|
||||||
|
const localName = new Intl.DisplayNames([code], { type: 'language' });
|
||||||
|
const englishName = new Intl.DisplayNames('en', { type: 'language' });
|
||||||
|
return <div className='item' title={`${englishName.of(code)}`} key={`${index}`} data-value={`${code}`} data-detail={`${localName.of(code)}`}>
|
||||||
|
{`${code}`}
|
||||||
|
<div className='detail'>{`${localName.of(code)}`}</div>
|
||||||
|
</div>;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedHandleFieldChange = _.debounce(this.handleFieldChange, 500);
|
||||||
|
|
||||||
|
return <div className='field language'>
|
||||||
|
<label>language</label>
|
||||||
|
<div className='value'>
|
||||||
|
<Combobox trigger='click'
|
||||||
|
className='language-dropdown'
|
||||||
|
default={this.props.metadata.lang || ''}
|
||||||
|
placeholder='en'
|
||||||
|
onSelect={(value)=>this.handleLanguage(value)}
|
||||||
|
onEntry={(e)=>{
|
||||||
|
e.target.setCustomValidity(''); //Clear the validation popup while typing
|
||||||
|
debouncedHandleFieldChange('lang', e);
|
||||||
|
}}
|
||||||
|
options={listLanguages()}
|
||||||
|
autoSuggest={{
|
||||||
|
suggestMethod : 'startsWith',
|
||||||
|
clearAutoSuggestOnClick : true,
|
||||||
|
filterOn : ['data-value', 'data-detail', 'title']
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</Combobox>
|
||||||
|
<small>Sets the HTML Lang property for your brew. May affect hyphenation or spellcheck.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
renderRenderOptions : function(){
|
renderRenderOptions : function(){
|
||||||
if(!global.enable_v3) return;
|
if(!global.enable_v3) return;
|
||||||
|
|
||||||
@@ -297,6 +350,8 @@ const MetadataEditor = createClass({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{this.renderLanguageDropdown()}
|
||||||
|
|
||||||
{this.renderThemeDropdown()}
|
{this.renderThemeDropdown()}
|
||||||
|
|
||||||
{this.renderRenderOptions()}
|
{this.renderRenderOptions()}
|
||||||
@@ -311,7 +366,7 @@ const MetadataEditor = createClass({
|
|||||||
validators={[(v)=>!this.props.metadata.authors?.includes(v)]}
|
validators={[(v)=>!this.props.metadata.authors?.includes(v)]}
|
||||||
placeholder='invite author' unique={true}
|
placeholder='invite author' unique={true}
|
||||||
values={this.props.metadata.invitedAuthors}
|
values={this.props.metadata.invitedAuthors}
|
||||||
notes={['Invited authors are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
|
notes={['Invited author usernames are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
|
||||||
onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/>
|
onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|||||||
@@ -35,13 +35,16 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 5 0 200px;
|
flex: 5 0 200px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.field{
|
.field{
|
||||||
display : flex;
|
display : flex;
|
||||||
flex-wrap : wrap;
|
flex-wrap : wrap;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
min-width : 200px;
|
min-width : 200px;
|
||||||
|
position : relative;
|
||||||
&>label{
|
&>label{
|
||||||
width : 80px;
|
width : 80px;
|
||||||
font-size : 11px;
|
font-size : 11px;
|
||||||
@@ -58,6 +61,9 @@
|
|||||||
}
|
}
|
||||||
input[type='text'], textarea {
|
input[type='text'], textarea {
|
||||||
border : 1px solid gray;
|
border : 1px solid gray;
|
||||||
|
&:focus {
|
||||||
|
outline: 1px solid #444;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.thumbnail{
|
&.thumbnail{
|
||||||
height : 1.4em;
|
height : 1.4em;
|
||||||
@@ -89,9 +95,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.language .language-dropdown {
|
||||||
|
max-width : 150px;
|
||||||
|
z-index : 200;
|
||||||
|
}
|
||||||
small {
|
small {
|
||||||
font-size : 0.6em;
|
font-size : 0.6em;
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
|
line-height : 1.4em;
|
||||||
|
display : inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,9 +171,8 @@
|
|||||||
font-size : 13.33px;
|
font-size : 13.33px;
|
||||||
.navDropdownContainer {
|
.navDropdownContainer {
|
||||||
background-color : white;
|
background-color : white;
|
||||||
width : 100%;
|
|
||||||
position : relative;
|
position : relative;
|
||||||
z-index : 500;
|
z-index : 100;
|
||||||
&.disabled {
|
&.disabled {
|
||||||
font-style :italic;
|
font-style :italic;
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
@@ -182,24 +193,51 @@
|
|||||||
}
|
}
|
||||||
.navDropdown {
|
.navDropdown {
|
||||||
box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3);
|
box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3);
|
||||||
position : absolute;
|
position : absolute;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
.item {
|
.item {
|
||||||
padding : 3px 3px;
|
padding : 3px 3px;
|
||||||
border-top : 1px solid rgb(118, 118, 118);
|
border-top : 1px solid rgb(118, 118, 118);
|
||||||
position : relative;
|
position : relative;
|
||||||
overflow : hidden;
|
overflow : visible;
|
||||||
background-color : white;
|
background-color : white;
|
||||||
|
.preview {
|
||||||
|
display : flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background : #ccc;
|
||||||
|
border-radius : 5px;
|
||||||
|
box-shadow : 0 0 5px black;
|
||||||
|
width : 200px;
|
||||||
|
color :black;
|
||||||
|
position : absolute;
|
||||||
|
top : 0;
|
||||||
|
right : 0;
|
||||||
|
opacity : 0;
|
||||||
|
transition : opacity 250ms ease;
|
||||||
|
z-index : 1;
|
||||||
|
overflow :hidden;
|
||||||
|
h6 {
|
||||||
|
font-weight : 900;
|
||||||
|
padding-inline:1em;
|
||||||
|
padding-block :.5em;
|
||||||
|
border-bottom :2px solid hsl(0,0%,40%);
|
||||||
|
}
|
||||||
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color : @blue;
|
background-color : @blue;
|
||||||
color : white;
|
color : white;
|
||||||
}
|
}
|
||||||
img {
|
&:hover > .preview {
|
||||||
mask-image : linear-gradient(90deg, transparent, black 20%);
|
opacity: 1;
|
||||||
|
}
|
||||||
|
>img {
|
||||||
|
mask-image : linear-gradient(90deg, transparent, black 20%);
|
||||||
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
|
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
|
||||||
position : absolute;
|
position : absolute;
|
||||||
left : ~"max(100px, 100% - 300px)";
|
right : 0;
|
||||||
top : 0px;
|
top : 0px;
|
||||||
|
width : 50%;
|
||||||
|
height : 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
language : [
|
lang : [
|
||||||
(value)=>{
|
(value)=>{
|
||||||
return new RegExp(/[a-z]{2,3}(-.*)?/).test(value || '') === false ? 'Invalid language code.' : null;
|
return new RegExp(/^([a-zA-Z]{2,3})(-[a-zA-Z]{4})?(-(?:[0-9]{3}|[a-zA-Z]{2}))?$/).test(value) === false && (value.length > 0) ? 'Invalid language code.' : null;
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -163,15 +163,23 @@ const SnippetGroup = createClass({
|
|||||||
onSnippetClick : function(){},
|
onSnippetClick : function(){},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSnippetClick : function(snippet){
|
handleSnippetClick : function(e, snippet){
|
||||||
|
e.stopPropagation();
|
||||||
this.props.onSnippetClick(execute(snippet.gen, this.props.brew));
|
this.props.onSnippetClick(execute(snippet.gen, this.props.brew));
|
||||||
},
|
},
|
||||||
renderSnippets : function(){
|
renderSnippets : function(snippets){
|
||||||
return _.map(this.props.snippets, (snippet)=>{
|
return _.map(snippets, (snippet)=>{
|
||||||
return <div className='snippet' key={snippet.name} onClick={()=>this.handleSnippetClick(snippet)}>
|
return <div className='snippet' key={snippet.name} onClick={(e)=>this.handleSnippetClick(e, snippet)}>
|
||||||
<i className={snippet.icon} />
|
<i className={snippet.icon} />
|
||||||
{snippet.name}
|
<span className='name'>{snippet.name}</span>
|
||||||
|
{snippet.experimental && <span className='beta'>beta</span>}
|
||||||
|
{snippet.subsnippets && <>
|
||||||
|
<i className='fas fa-caret-right'></i>
|
||||||
|
<div className='dropdown side'>
|
||||||
|
{this.renderSnippets(snippet.subsnippets)}
|
||||||
|
</div></>}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -182,7 +190,7 @@ const SnippetGroup = createClass({
|
|||||||
<span className='groupName'>{this.props.groupName}</span>
|
<span className='groupName'>{this.props.groupName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='dropdown'>
|
<div className='dropdown'>
|
||||||
{this.renderSnippets()}
|
{this.renderSnippets(this.props.snippets)}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
@import (less) './client/icons/customIcons.less';
|
||||||
.snippetBar{
|
.snippetBar{
|
||||||
@menuHeight : 25px;
|
@menuHeight : 25px;
|
||||||
position : relative;
|
position : relative;
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
.snippetGroup{
|
.snippetGroup{
|
||||||
border-right : 1px solid black;
|
border-right : 1px solid black;
|
||||||
&:hover{
|
&:hover{
|
||||||
.dropdown{
|
&>.dropdown{
|
||||||
visibility : visible;
|
visibility : visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,16 +96,47 @@
|
|||||||
padding : 0px;
|
padding : 0px;
|
||||||
background-color : #ddd;
|
background-color : #ddd;
|
||||||
.snippet{
|
.snippet{
|
||||||
|
position: relative;
|
||||||
.animate(background-color);
|
.animate(background-color);
|
||||||
padding : 5px;
|
display : flex;
|
||||||
cursor : pointer;
|
align-items : center;
|
||||||
font-size : 10px;
|
min-width : max-content;
|
||||||
|
padding : 5px;
|
||||||
|
cursor : pointer;
|
||||||
|
font-size : 10px;
|
||||||
i{
|
i{
|
||||||
margin-right : 8px;
|
margin-right : 8px;
|
||||||
font-size : 1.2em;
|
font-size : 1.2em;
|
||||||
|
height : 1.2em;
|
||||||
|
&~i{
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
margin-right : auto;
|
||||||
|
}
|
||||||
|
.beta {
|
||||||
|
color : white;
|
||||||
|
padding : 4px 6px;
|
||||||
|
line-height : 1em;
|
||||||
|
margin-left : 5px;
|
||||||
|
align-self : center;
|
||||||
|
background : grey;
|
||||||
|
border-radius : 12px;
|
||||||
|
font-family : monospace;
|
||||||
}
|
}
|
||||||
&:hover{
|
&:hover{
|
||||||
background-color : #999;
|
background-color : #999;
|
||||||
|
&>.dropdown{
|
||||||
|
visibility : visible;
|
||||||
|
&.side {
|
||||||
|
left: 100%;
|
||||||
|
top: 0%;
|
||||||
|
margin-left:0;
|
||||||
|
box-shadow: -1px 1px 2px 0px #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 305 KiB |
8
client/homebrew/googleDrive.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg viewBox="0 0 87.3 78" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m6.6 66.85 3.85 6.65c.8 1.4 1.95 2.5 3.3 3.3l13.75-23.8h-27.5c0 1.55.4 3.1 1.2 4.5z" fill="#0066da"/>
|
||||||
|
<path d="m43.65 25-13.75-23.8c-1.35.8-2.5 1.9-3.3 3.3l-25.4 44a9.06 9.06 0 0 0 -1.2 4.5h27.5z" fill="#00ac47"/>
|
||||||
|
<path d="m73.55 76.8c1.35-.8 2.5-1.9 3.3-3.3l1.6-2.75 7.65-13.25c.8-1.4 1.2-2.95 1.2-4.5h-27.502l5.852 11.5z" fill="#ea4335"/>
|
||||||
|
<path d="m43.65 25 13.75-23.8c-1.35-.8-2.9-1.2-4.5-1.2h-18.5c-1.6 0-3.15.45-4.5 1.2z" fill="#00832d"/>
|
||||||
|
<path d="m59.8 53h-32.3l-13.75 23.8c1.35.8 2.9 1.2 4.5 1.2h50.8c1.6 0 3.15-.45 4.5-1.2z" fill="#2684fc"/>
|
||||||
|
<path d="m73.4 26.5-12.7-22c-.8-1.4-1.95-2.5-3.3-3.3l-13.75 23.8 16.15 28h27.45c0-1.55-.4-3.1-1.2-4.5z" fill="#ffba00"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 755 B |
|
Before Width: | Height: | Size: 17 KiB |
@@ -47,6 +47,7 @@ const Homebrew = createClass({
|
|||||||
editId : null,
|
editId : null,
|
||||||
createdAt : null,
|
createdAt : null,
|
||||||
updatedAt : null,
|
updatedAt : null,
|
||||||
|
lang : ''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const Account = createClass({
|
|||||||
if(global.account){
|
if(global.account){
|
||||||
return <Nav.dropdown>
|
return <Nav.dropdown>
|
||||||
<Nav.item
|
<Nav.item
|
||||||
className='account'
|
className='account username'
|
||||||
color='orange'
|
color='orange'
|
||||||
icon='fas fa-user'
|
icon='fas fa-user'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -82,4 +82,4 @@ const ErrorNavItem = createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = ErrorNavItem;
|
module.exports = ErrorNavItem;
|
||||||
|
|||||||
@@ -1,77 +1,75 @@
|
|||||||
.navItem {
|
.navItem.error {
|
||||||
&.error {
|
position : relative;
|
||||||
position : relative;
|
background-color : @red;
|
||||||
background-color : @red;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.errorContainer{
|
.errorContainer{
|
||||||
animation-name: glideDown;
|
animation-name: glideDown;
|
||||||
animation-duration: 0.4s;
|
animation-duration: 0.4s;
|
||||||
position : absolute;
|
position : absolute;
|
||||||
top : 100%;
|
top : 100%;
|
||||||
left : 50%;
|
left : 50%;
|
||||||
z-index : 1000;
|
z-index : 1000;
|
||||||
width : 140px;
|
width : 140px;
|
||||||
padding : 3px;
|
padding : 3px;
|
||||||
color : white;
|
color : white;
|
||||||
|
background-color : #333;
|
||||||
|
border : 3px solid #444;
|
||||||
|
border-radius : 5px;
|
||||||
|
transform : translate(-50% + 3px, 10px);
|
||||||
|
text-align : center;
|
||||||
|
font-size : 10px;
|
||||||
|
font-weight : 800;
|
||||||
|
text-transform : uppercase;
|
||||||
|
a{
|
||||||
|
color : @teal;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
position: absolute;
|
||||||
|
border-left: 10px solid transparent;
|
||||||
|
border-right: 10px solid transparent;
|
||||||
|
border-top: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid #444;
|
||||||
|
left: 53px;
|
||||||
|
top: -23px;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
position: absolute;
|
||||||
|
border-left: 10px solid transparent;
|
||||||
|
border-right: 10px solid transparent;
|
||||||
|
border-top: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid #333;
|
||||||
|
left: 53px;
|
||||||
|
top: -19px;
|
||||||
|
}
|
||||||
|
.deny {
|
||||||
|
width : 48%;
|
||||||
|
margin : 1px;
|
||||||
|
padding : 5px;
|
||||||
background-color : #333;
|
background-color : #333;
|
||||||
border : 3px solid #444;
|
display : inline-block;
|
||||||
border-radius : 5px;
|
border-left : 1px solid #666;
|
||||||
transform : translate(-50% + 3px, 10px);
|
.animate(background-color);
|
||||||
text-align : center;
|
&:hover{
|
||||||
font-size : 10px;
|
background-color : red;
|
||||||
font-weight : 800;
|
|
||||||
text-transform : uppercase;
|
|
||||||
a{
|
|
||||||
color : @teal;
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
position: absolute;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #444;
|
|
||||||
left: 53px;
|
|
||||||
top: -23px;
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
position: absolute;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #333;
|
|
||||||
left: 53px;
|
|
||||||
top: -19px;
|
|
||||||
}
|
|
||||||
.deny {
|
|
||||||
width : 48%;
|
|
||||||
margin : 1px;
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #333;
|
|
||||||
display : inline-block;
|
|
||||||
border-left : 1px solid #666;
|
|
||||||
.animate(background-color);
|
|
||||||
&:hover{
|
|
||||||
background-color : red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.confirm {
|
|
||||||
width : 48%;
|
|
||||||
margin : 1px;
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #333;
|
|
||||||
display : inline-block;
|
|
||||||
color : white;
|
|
||||||
.animate(background-color);
|
|
||||||
&:hover{
|
|
||||||
background-color : teal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.confirm {
|
||||||
|
width : 48%;
|
||||||
|
margin : 1px;
|
||||||
|
padding : 5px;
|
||||||
|
background-color : #333;
|
||||||
|
display : inline-block;
|
||||||
|
color : white;
|
||||||
|
.animate(background-color);
|
||||||
|
&:hover{
|
||||||
|
background-color : teal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -187,4 +187,7 @@
|
|||||||
.account.navItem{
|
.account.navItem{
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
.account.username.navItem{
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const cx = require('classnames');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const request = require('../../../../utils/request-middleware.js');
|
const request = require('../../../../utils/request-middleware.js');
|
||||||
|
|
||||||
const googleDriveIcon = require('../../../../googleDrive.png');
|
const googleDriveIcon = require('../../../../googleDrive.svg');
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
const BrewItem = createClass({
|
const BrewItem = createClass({
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.googleDriveIcon {
|
.googleDriveIcon {
|
||||||
height : 20px;
|
height : 18px;
|
||||||
padding : 0px;
|
padding : 0px;
|
||||||
margin : -5px;
|
margin : -5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,12 +219,13 @@ const ListPage = createClass({
|
|||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <div className='listPage sitePage'>
|
return <div className='listPage sitePage'>
|
||||||
|
{/*<style>@layer V3_5ePHB, bundle;</style>*/}
|
||||||
<link href='/themes/V3/5ePHB/style.css' rel='stylesheet'/>
|
<link href='/themes/V3/5ePHB/style.css' rel='stylesheet'/>
|
||||||
{this.props.navItems}
|
{this.props.navItems}
|
||||||
{this.renderSortOptions()}
|
{this.renderSortOptions()}
|
||||||
|
|
||||||
<div className='content V3'>
|
<div className='content V3'>
|
||||||
<div className='phb page'>
|
<div className='page'>
|
||||||
{this.renderBrewCollection(this.state.brewCollection)}
|
{this.renderBrewCollection(this.state.brewCollection)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,14 +10,14 @@
|
|||||||
-moz-column-width : auto;
|
-moz-column-width : auto;
|
||||||
-webkit-column-gap : auto;
|
-webkit-column-gap : auto;
|
||||||
-moz-column-gap : auto;
|
-moz-column-gap : auto;
|
||||||
|
height : auto;
|
||||||
|
min-height : 279.4mm;
|
||||||
|
margin : 20px auto;
|
||||||
}
|
}
|
||||||
.listPage{
|
.listPage{
|
||||||
.content{
|
.content{
|
||||||
.phb{
|
.page{
|
||||||
.noColumns();
|
.noColumns() !important; //Needed to override PHB Theme since this is on a lower @layer
|
||||||
height : auto;
|
|
||||||
min-height : 279.4mm;
|
|
||||||
margin : 20px auto;
|
|
||||||
&::after{
|
&::after{
|
||||||
display : none;
|
display : none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ const Markdown = require('naturalcrit/markdown.js');
|
|||||||
|
|
||||||
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
|
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
|
||||||
|
|
||||||
const googleDriveActive = require('../../googleDrive.png');
|
const googleDriveIcon = require('../../googleDrive.svg');
|
||||||
const googleDriveInactive = require('../../googleDriveMono.png');
|
|
||||||
|
|
||||||
const SAVE_TIMEOUT = 3000;
|
const SAVE_TIMEOUT = 3000;
|
||||||
|
|
||||||
@@ -222,10 +221,7 @@ const EditPage = createClass({
|
|||||||
|
|
||||||
renderGoogleDriveIcon : function(){
|
renderGoogleDriveIcon : function(){
|
||||||
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
|
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
|
||||||
{this.state.saveGoogle
|
<img src={googleDriveIcon} className={this.state.saveGoogle ? '' : 'inactive'} alt='Google Drive icon'/>
|
||||||
? <img src={googleDriveActive} alt='googleDriveActive'/>
|
|
||||||
: <img src={googleDriveInactive} alt='googleDriveInactive'/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{this.state.confirmGoogleTransfer &&
|
{this.state.confirmGoogleTransfer &&
|
||||||
<div className='errorContainer' onClick={this.closeAlerts}>
|
<div className='errorContainer' onClick={this.closeAlerts}>
|
||||||
@@ -258,6 +254,15 @@ const EditPage = createClass({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{this.state.alertTrashedGoogleBrew &&
|
||||||
|
<div className='errorContainer' onClick={this.closeAlerts}>
|
||||||
|
This brew is currently in your Trash folder on Google Drive!<br />If you want to keep it, make sure to move it before it is deleted permanently!<br />
|
||||||
|
<div className='confirm'>
|
||||||
|
OK
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -339,16 +344,6 @@ const EditPage = createClass({
|
|||||||
const shareLink = this.processShareId();
|
const shareLink = this.processShareId();
|
||||||
|
|
||||||
return <Navbar>
|
return <Navbar>
|
||||||
|
|
||||||
{this.state.alertTrashedGoogleBrew &&
|
|
||||||
<div className='errorContainer' onClick={this.closeAlerts}>
|
|
||||||
This brew is currently in your Trash folder on Google Drive!<br />If you want to keep it, make sure to move it before it is deleted permanently!<br />
|
|
||||||
<div className='confirm'>
|
|
||||||
OK
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
|
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
@@ -402,7 +397,14 @@ const EditPage = createClass({
|
|||||||
reportError={this.errorReported}
|
reportError={this.errorReported}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors} />
|
<BrewRenderer
|
||||||
|
text={this.state.brew.text}
|
||||||
|
style={this.state.brew.style}
|
||||||
|
renderer={this.state.brew.renderer}
|
||||||
|
theme={this.state.brew.theme}
|
||||||
|
errors={this.state.htmlErrors}
|
||||||
|
lang={this.state.brew.lang}
|
||||||
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -18,8 +18,12 @@
|
|||||||
position : relative;
|
position : relative;
|
||||||
}
|
}
|
||||||
.googleDriveStorage img{
|
.googleDriveStorage img{
|
||||||
height : 20px;
|
height : 18px;
|
||||||
padding : 0px;
|
padding : 0px;
|
||||||
margin : -5px;
|
margin : -5px;
|
||||||
|
|
||||||
|
&.inactive {
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ After clicking the "Print" item in the navbar a new page will open and a print d
|
|||||||
If you want to save ink or have a monochrome printer, add the **PRINT → {{fas,fa-tint}} Ink Friendly** snippet to your brew!
|
If you want to save ink or have a monochrome printer, add the **PRINT → {{fas,fa-tint}} Ink Friendly** snippet to your brew!
|
||||||
}}
|
}}
|
||||||
|
|
||||||
 {position:absolute,bottom:20px,left:130px,width:220px}
|
 {position:absolute,bottom:20px,left:130px,width:220px}
|
||||||
|
|
||||||
{{artist,bottom:160px,left:100px
|
{{artist,bottom:160px,left:100px
|
||||||
##### Homebrew Mug
|
##### Homebrew Mug
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const NewPage = createClass({
|
|||||||
// brew.description = metaStorage?.description || this.state.brew.description;
|
// brew.description = metaStorage?.description || this.state.brew.description;
|
||||||
brew.renderer = metaStorage?.renderer ?? brew.renderer;
|
brew.renderer = metaStorage?.renderer ?? brew.renderer;
|
||||||
brew.theme = metaStorage?.theme ?? brew.theme;
|
brew.theme = metaStorage?.theme ?? brew.theme;
|
||||||
|
brew.lang = metaStorage?.lang ?? brew.lang;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
brew : brew
|
brew : brew
|
||||||
@@ -70,7 +71,7 @@ const NewPage = createClass({
|
|||||||
localStorage.setItem(BREWKEY, brew.text);
|
localStorage.setItem(BREWKEY, brew.text);
|
||||||
if(brew.style)
|
if(brew.style)
|
||||||
localStorage.setItem(STYLEKEY, brew.style);
|
localStorage.setItem(STYLEKEY, brew.style);
|
||||||
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme }));
|
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme, 'lang': brew.lang }));
|
||||||
},
|
},
|
||||||
componentWillUnmount : function() {
|
componentWillUnmount : function() {
|
||||||
document.removeEventListener('keydown', this.handleControlKeys);
|
document.removeEventListener('keydown', this.handleControlKeys);
|
||||||
@@ -114,13 +115,16 @@ const NewPage = createClass({
|
|||||||
handleMetaChange : function(metadata){
|
handleMetaChange : function(metadata){
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, ...metadata },
|
brew : { ...prevState.brew, ...metadata },
|
||||||
}));
|
}), ()=>{
|
||||||
localStorage.setItem(METAKEY, JSON.stringify({
|
localStorage.setItem(METAKEY, JSON.stringify({
|
||||||
// 'title' : this.state.brew.title,
|
// 'title' : this.state.brew.title,
|
||||||
// 'description' : this.state.brew.description,
|
// 'description' : this.state.brew.description,
|
||||||
'renderer' : this.state.brew.renderer,
|
'renderer' : this.state.brew.renderer,
|
||||||
'theme' : this.state.brew.theme
|
'theme' : this.state.brew.theme,
|
||||||
}));
|
'lang' : this.state.brew.lang
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
;
|
||||||
},
|
},
|
||||||
|
|
||||||
save : async function(){
|
save : async function(){
|
||||||
@@ -211,7 +215,7 @@ const NewPage = createClass({
|
|||||||
onMetaChange={this.handleMetaChange}
|
onMetaChange={this.handleMetaChange}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors}/>
|
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} lang={this.state.brew.lang} errors={this.state.htmlErrors}/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ const PrintPage = createClass({
|
|||||||
|
|
||||||
renderStyle : function() {
|
renderStyle : function() {
|
||||||
if(!this.state.brew.style) return;
|
if(!this.state.brew.style) return;
|
||||||
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.state.brew.style} </style>` }} />;
|
//return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>@layer styleTab {\n${this.state.brew.style}\n} </style>` }} />;
|
||||||
|
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>\n${this.state.brew.style}\n</style>` }} />;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderPages : function(){
|
renderPages : function(){
|
||||||
|
|||||||
1
client/icons/Davek.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 791.04 953.29"><title>Davek</title><g id="Layer_2" data-name="Layer 2"><g id="Davek"><path d="M178.41,13.46a19.33,19.33,0,0,0-4.71,5.38q8.07,6.07,13.46,6.07a8.27,8.27,0,0,0,4.71-1.35,130.23,130.23,0,0,0,16.83-7.07,74.55,74.55,0,0,1,18.85-6.39h2.7q8.07,0,14.81,8.74a944.19,944.19,0,0,0,95.6,4.72q19.5,0,38.37-.67,69.33-2,139.68-5.72t139.7-5.06q16.82-.64,34.34-.66,50.49,0,98.29,3.36-17.5,12.12-22.55,31.64t-5,33.66q.64,22.89.66,45.1,0,47.13-3.36,97-6.07,74.05-9.78,148.11t-5,146.09v17.51a766.1,766.1,0,0,0,8.75,118.48,38.57,38.57,0,0,0-4,17.51,30.94,30.94,0,0,0,.67,6.06q2,12.12,3.36,23.22c.9,7.42,1.57,14.92,2,22.55v3.37a57.93,57.93,0,0,1-3.36,19.52c.43,4.5.67,8.77.67,12.8a260.65,260.65,0,0,1-2.7,37,344.26,344.26,0,0,0-4,52.52,133.5,133.5,0,0,0,8.09,45.44q8.07,22.57,33,36.68-6.06,8.78-20.19,8.77H762.1c-4.5-.45-8.53-.69-12.12-.69a78.11,78.11,0,0,0-21.54,2.7,579.1,579.1,0,0,0-63.64,3.71q-33.31,3.71-67.65,6.39t-68.66,3.37h-4a188.05,188.05,0,0,1-59.92-9.43q20.19-4,39.06-23.22t20.19-47.46q11.44-22.21,11.45-49.82a320.44,320.44,0,0,1,3.36-49.15q-9.45-4.69-10.09-8.75v-2.7a73,73,0,0,1,.66-8.74,105.81,105.81,0,0,0,3.37-12.8,7.49,7.49,0,0,0,.68-3.37q0-4.7-4.05-10.09c.45-4.93.69-10.1.69-15.48a311.71,311.71,0,0,0-3.37-46.45,207.31,207.31,0,0,1-1.35-24.25,274.58,274.58,0,0,1,4-45.1l15.5,6.73q-3.37-17.49-3.37-41.07,0-24.89,8.75-44.44a27.73,27.73,0,0,0,2-9.43,15.32,15.32,0,0,0-3.36-10.09,60.75,60.75,0,0,1-10.1-15.48l-7.39,6.73q2.67-47.79,8.74-99,3.35-33.63,3.37-65.29,0-14.81-.69-29a205.09,205.09,0,0,1-4-41.74,190.26,190.26,0,0,1,2-26.92q4-37,14.81-67.33a25.14,25.14,0,0,1-2.68-11.43,31.13,31.13,0,0,1,.66-6.07V140q0-6.72-8.74-10.09-3.37-16.83-5.73-31.3T521.07,77.41q-55.2,2.7-115.78,4.71-19.55.7-39.72.69-38.38,0-74.06-2.7c-5.4,4.5-8.08,9.21-8.08,14.14v1.34a41.5,41.5,0,0,0,4.37,15.49q3.7,7.4,7.4,15.16a35,35,0,0,1,3.71,15.13q32.31,34.35,64,68.68a335.89,335.89,0,0,1,51.83,73.38q13.46,7.4,18.51,17.49t10.11,19.87q5.06,9.78,10.1,18.85t16.5,11.78v12.12a194.5,194.5,0,0,1-37.38-4q-20.52-4-40.73-6.73a114.48,114.48,0,0,0-17.49-1.35,97.2,97.2,0,0,0-20.2,2q-17.52,4.05-31,20.19-16.84-1.35-27.27-9.75a76.13,76.13,0,0,1-17.51-20.2q-7.06-11.76-14.47-24.9a79.77,79.77,0,0,0-18.84-22.57A305.87,305.87,0,0,1,177.73,237q-28.29-33.67-54.54-69T68,99.31A381.16,381.16,0,0,0,0,38.37q12.79,0,22.89-9.75A190.69,190.69,0,0,1,44.76,10.44Q56.54,2,68.66,0H72Q82.8,0,97,10.76Z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
1
client/icons/Iokharic.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 428.05 941.17"><title>Iokharic</title><g id="Layer_2" data-name="Layer 2"><g id="Iokharic"><path d="M334.76,909.61V259.3l2.74-89.18c3.43,0,6.18-8.23,7.55-24.69,3.43,0,7.55-8.92,13.72-27.44,13-11,19.89-21.27,19.89-31.56,0-13-5.48-20.58-17.15-23.32l-30.87,2.74H320.36c-21.27,13-39.79,22.64-56.94,27.44h-37c-11.67,0-26.76,7.55-46,22q-12.34,0-30.86,16.46c-10.29,0-40.48,26.75-91.93,80.95,0,8.23-6.17,21.26-18.52,38.41l-3.43,15.78v41.84L67.23,343c2.74,0,9.6,6.86,19.89,19.9,24,18.52,36.36,30.86,36.36,38.41l-12.35,10.29H105c-24.7-15.78-45.28-32.93-62.43-52.13L15.78,316.92,0,266.85c3.43-17.84,7.55-29.5,13.72-35v-11c0-18.52,7.55-39.79,22-63.8,0-9.6,8.23-21.27,24.7-34.3,0-9.6,15.77-26.07,46.64-50.08,19.9-16.46,46-28.12,76.83-35,5.49-6.86,21.27-14.41,46.65-21.95C238,5.49,251.07,0,270.28,0h137.2c8.91,0,15.77,8.23,20.57,24V40.47l-5.48,8.23V166c0,17.15-7.55,31.55-21.95,43.22v41.15l-2.75,24.7q0,9.26,24.7,30.87v38.41c0,10.29-4.81,19.9-15.09,28.82h-6.86V558.39c0,55.57-4.81,97.41-15.1,124.16-4.8,2.75-7.54,19.21-9.6,48.71l2.74,17.15-2.74,76.14v30.19q0,32.93-32.93,86.43C337.5,937.74,334.76,926.76,334.76,909.61Z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/icons/Rellanic.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 527.7 940.25"><title>Rellanic</title><g id="Layer_2" data-name="Layer 2"><g id="Rellanic"><path d="M527.7,5.45q-3.83,19.65-15,30.56a129.61,129.61,0,0,1-26.46,19.64q-9.84,6.56-31.66,15.28-19.63,7.65-31.64,16.38Q380.33,103.69,342.16,108a468.46,468.46,0,0,1-54,3.28q-15.83,0-30.56-1.1a53.19,53.19,0,0,0-20.19-6.55H217.74q-7.12,1.11-21.29,1.1a51.67,51.67,0,0,1-20.18-4.36q8.72,19.65,25.63,29.46,14.19,8.74,28.38,29.47a634.05,634.05,0,0,1,98.78,90.58l91.12,103.69a65.1,65.1,0,0,0-.54,8.19,42.47,42.47,0,0,0,.54,7.09c.73,1.82,1.27,3.29,1.64,4.37q7.08,8.75,10.92,12,1.62,1.1,12.55,14.19a14,14,0,0,1,3.27,6.55,9.75,9.75,0,0,1,1.1,4.37,9.62,9.62,0,0,1-1.1,4.36q35.46,43.66,51.3,89.5,3.25,9.82,5.45,19.64a288.59,288.59,0,0,1,10.37,68.75v8.19a296,296,0,0,1-9.81,76.94q-7.12,27.3-24,77.5L418,831.65Q383,872,344.88,899.31a243.27,243.27,0,0,1-90.59,38.19,179.84,179.84,0,0,1-31.64,2.75q-38.78,0-81.87-15.84A293.78,293.78,0,0,1,78,886.22a312.61,312.61,0,0,1-51.85-48,300.52,300.52,0,0,0-18-46.94,60.18,60.18,0,0,1-4.92-13.64,82.36,82.36,0,0,1-2.19-19.11,104.89,104.89,0,0,1,.56-10.91,176.12,176.12,0,0,1-1.64-24,199.79,199.79,0,0,1,2.72-32.74Q5.45,663,5.45,645a103.71,103.71,0,0,0-.54-10.92,242.44,242.44,0,0,1,50.74-67.66,646.83,646.83,0,0,0,57.86-61.12q11.44-10.89,25.09-13.1A88.3,88.3,0,0,1,163.71,489q14.17-1.11,29.46-1.1a108.11,108.11,0,0,0,28.38-7.63q17.44,8.75,27.29,12a124.47,124.47,0,0,1,28.38,13.1q8.71,4.38,23.46,17.46,9.29,9.86,17.47,28.38,7.07,12,9.27,21.83a35.16,35.16,0,0,1,1.64,9.83V585a80.23,80.23,0,0,1-8.73,27.28q-8.2,14.19-18,22.93a166.18,166.18,0,0,1-19.65,19.64q-13.1,8.74-20.72,13.1l-7.65-4.37v-1.64q0-12,6.55-18-8.17-6.55-10.36-10.92l-2.18-8.73c0-2.18-.74-5.81-2.19-10.91v-3.29a38,38,0,0,0-3.82-7.63,196.53,196.53,0,0,0-33.84-40.39Q185.53,542.43,162.61,537a163.71,163.71,0,0,0-50.75,9.81q-25.08,8.76-32.2,36Q67.12,615.56,67.13,654.3a256,256,0,0,0,3.26,39.83,176.75,176.75,0,0,0,5.47,28.38Q88.37,770,122.78,812a452.22,452.22,0,0,0,103.13,58.94,153.57,153.57,0,0,0,107,5.45q25.63-12,37.66-27.28,13.62-14.21,23.46-34.93,10.36-18.57,20.2-39.29Q426.72,753.05,437.1,740q3.27-44.76,5.47-61.12a228.17,228.17,0,0,0,3.26-38.21,213.15,213.15,0,0,0-1.64-26.19,245.3,245.3,0,0,0-8.17-48q-2.2-8.17-4.93-16.36-9.27-30.55-34.92-61.12a70,70,0,0,0-2.18-18,29.12,29.12,0,0,0-4.37-10.37,175.28,175.28,0,0,0-17.46-29.48l-18.55-27.27q-12-16.38-16.38-28.38a282.35,282.35,0,0,1-27.81-28.37q-20.22-26.2-24-31.66Q269,295.76,260.29,286q-10.92-12-31.1-25.11-36.56-31.65-79.12-70.94-45.31-39.28-88.41-66.58-14.74-8.17-17.46-16.9a16.93,16.93,0,0,0-.54-3.83V99.87q0-8.73,6.54-19.11A102.47,102.47,0,0,1,63.3,61.12q9.27-9.82,12.56-18.56a223.6,223.6,0,0,1,38.73-3.27,271,271,0,0,1,40.93,3.27A367.15,367.15,0,0,0,215,47.48c6.91,0,13.64-.17,20.2-.56a45,45,0,0,0,21.27,5.47q17.44,0,25.65-1.1h22.93a77.75,77.75,0,0,1,24,7.65,114,114,0,0,1,27.82-3.29H364q27.25,2.2,39.29,2.19,16.34,0,36.55-5.45,19.1-6.55,27.83-22.93h2.72A20.48,20.48,0,0,0,484.58,24c2.17-4.71,6.17-7.09,12-7.09a26.6,26.6,0,0,1,4.92.54v-.54c0-1.08.72-3.46,2.19-7.11a36.74,36.74,0,0,1,6-6.54C512.57,1.1,515.12,0,517.32,0,521,0,524.41,1.82,527.7,5.45Z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
48
client/icons/book-front-cover.svg
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 541.53217 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg22127"
|
||||||
|
sodipodi:docname="book-front-cover.svg"
|
||||||
|
width="541.53217"
|
||||||
|
height="512"
|
||||||
|
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs22131" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview22129"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#111111"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.39257813"
|
||||||
|
inkscape:cx="-263.64179"
|
||||||
|
inkscape:cy="444.49751"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg22127" />
|
||||||
|
<!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||||
|
<g
|
||||||
|
id="g20308"
|
||||||
|
transform="matrix(3.7795276,0,0,3.7795276,-201.76367,-251.58203)">
|
||||||
|
<path
|
||||||
|
id="rect20232"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:17.9;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill;stop-color:#000000"
|
||||||
|
d="m 78.783305,66.564412 c -14.022889,0 -25.4,11.377111 -25.4,25.4 v 84.666668 c 0,14.02289 11.377111,25.4 25.4,25.4 h 76.199995 8.46667 c 4.68312,0 8.46667,-3.78355 8.46667,-8.46667 0,-4.68311 -3.78355,-8.46666 -8.46667,-8.46666 v -16.93334 c 4.68312,0 8.46667,-3.78355 8.46667,-8.46666 v -1.9327 c -0.0322,-0.27545 -0.0652,-0.54693 -0.0946,-0.83923 -0.17511,-1.74441 -0.30542,-3.81626 -0.37672,-6.02909 -0.18285,-5.67612 -0.29322,-5.86808 -0.63459,-6.62698 -0.74838,-1.66366 -2.65792,-3.64941 -4.38681,-4.49844 -1.41973,-0.69716 -0.72585,-0.45434 -1.20923,-0.51934 -0.47548,-0.0639 -2.54581,-0.13856 -6.47454,-0.14056 -0.0907,2.9929 -0.0862,4.81682 -0.58601,7.244 -0.28023,1.36071 -0.97957,3.42078 -2.40812,5.10356 -1.42519,1.67884 -2.81498,2.35811 -3.28145,2.61896 -3.14428,1.76375 -5.09549,2.43427 -9.41597,1.33997 -2.05224,-0.5197 -2.32631,-0.92288 -2.76159,-1.19527 -0.43528,-0.27239 -0.71007,-0.47684 -0.97461,-0.67593 -0.52909,-0.39816 -0.97871,-0.77171 -1.48622,-1.20664 -1.015,-0.86987 -2.20927,-1.95397 -3.6096,-3.26182 -2.80065,-2.61568 -6.38094,-6.09226 -10.18335,-9.90844 -6.19117,-6.21357 -9.5466,-9.59164 -11.7874,-12.16412 -1.1204,-1.28623 -2.03413,-2.38181 -2.90576,-4.03127 -0.87162,-1.64948 -1.40664,-4.21493 -1.40664,-5.61103 0,-1.4012 0.54783,-3.99366 1.42989,-5.64668 0.88206,-1.65304 1.8039,-2.74855 2.94142,-4.04679 2.27504,-2.59646 5.70131,-6.03358 12.03699,-12.369267 7.37691,-7.376888 10.87768,-11.090687 14.75208,-13.810527 1.45289,-1.019939 3.46378,-2.249133 6.08386,-2.580204 0.87337,-0.110323 1.8133,-0.120299 2.82412,0.0098 4.0433,0.520471 6.12413,2.832857 7.01973,3.728454 1.29782,1.297845 3.1373,4.826955 3.46852,7.049182 0.29817,2.00025 0.26393,3.770666 0.25993,6.212541 0.57954,0.0034 0.50388,0.0217 1.17564,0.0217 4.54211,0 8.44363,0.111537 11.991,0.50953 v -21.41004 c 0,-4.683115 -3.78355,-8.466667 -8.46667,-8.466667 h -8.46667 z m 0,101.599998 h 67.733335 v 16.93334 H 78.783305 c -4.683115,0 -8.466667,-3.78357 -8.466667,-8.46667 0,-4.68313 3.783552,-8.46667 8.466667,-8.46667 z" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#000000;stroke-width:17.9;stroke-linejoin:round;-inkscape-stroke:none;paint-order:stroke markers fill"
|
||||||
|
d="m 186.69094,157.95633 c 2.67243,-2.24871 7.17957,-9.39389 8.63888,-13.69528 1.03796,-3.05942 1.31928,-5.13546 1.33362,-9.84167 0.0278,-9.1246 -2.25302,-14.5915 -8.79325,-21.07662 -6.8535,-6.79576 -12.35348,-8.46107 -27.94423,-8.46107 -8.05417,0 -9.45684,-0.12924 -9.75203,-0.89852 -0.18964,-0.49417 -0.34479,-3.81715 -0.34479,-7.384389 0,-5.728497 -0.13266,-6.618534 -1.13607,-7.621956 -2.57777,-2.57775 -3.29907,-2.07141 -18.02212,12.651595 -12.64444,12.64444 -13.78771,13.94921 -13.78771,15.73575 0,1.78396 1.13629,3.08846 13.49078,15.48766 7.47518,7.50224 14.10644,13.69554 14.8715,13.88928 0.78576,0.19902 2.0096,-0.002 2.84016,-0.46789 1.42969,-0.80092 1.46523,-0.97351 1.74346,-8.46583 l 0.28402,-7.64825 h 8.52049 c 8.16738,0 8.65373,0.0655 11.73586,1.579 3.72428,1.82893 6.9202,5.12058 8.60236,8.86006 0.94352,2.09748 1.22898,4.1112 1.41901,10.01012 0.13083,4.06143 0.49647,7.70394 0.81253,8.09446 0.94895,1.17251 3.64241,0.80611 5.48753,-0.74645 z"
|
||||||
|
id="path20297" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
53
client/icons/book-inside-cover.svg
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 704.00001 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg22127"
|
||||||
|
sodipodi:docname="book-inside-cover.svg"
|
||||||
|
width="704"
|
||||||
|
height="512"
|
||||||
|
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||||
|
inkscape:export-filename="InsideCover3.png"
|
||||||
|
inkscape:export-xdpi="300"
|
||||||
|
inkscape:export-ydpi="300"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs22131" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview22129"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#111111"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.47274833"
|
||||||
|
inkscape:cx="83.55397"
|
||||||
|
inkscape:cy="178.74204"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg22127" />
|
||||||
|
<!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||||
|
<path
|
||||||
|
id="path2161-6"
|
||||||
|
style="color:#000000;fill:#000000;stroke-width:1;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||||
|
d="M 208,0 C 147.0078,0 94.429433,14.25071 60.367188,26.66992 23.520854,39.96036 0,76.16076 0,112.95896 v 317.8321 c 0,59.8499 56.949847,92.6546 107.47266,76.6035 l -0.1543,0.049 c 26.46715,-8.335 74.84649,-18.3965 100.68164,-18.3965 17.25807,0 61.31688,10.6183 85.14453,18.8438 l 0.0508,0.018 0.0527,0.018 c 19.82627,6.5858 40.84117,4.9222 58.99804,-3.0762 18.04267,7.8799 38.84257,9.6126 58.33594,3.1328 l 0.13672,-0.045 0.13672,-0.047 c 23.88445,-8.0588 67.88646,-18.8437 85.14453,-18.8437 25.83515,0 74.22549,10.0266 100.68164,18.3964 l 0.1543,0.049 0.15625,0.049 C 647.13371,523.05316 704,490.64216 704,430.79226 v -317.8321 c 0,-36.8274 -23.49583,-72.8235 -60.00977,-86.25583 l -0.16015,-0.0606 -0.16211,-0.0566 C 609.79193,14.33005 557.11269,0.0012 496,0.0012 434.5671,0.0012 387.12553,14.01354 352,34.94261 316.87446,14.01344 269.4331,0.0012 208,0.0012 Z m 0,32.00977 c 58.3999,0 103.40004,18.89469 123,33.63279 3.3,2.4564 5,6.4246 5,10.3926 v 356.5508 c 0,10.7702 -11.70041,18.2326 -22.40039,14.6426 -26.59996,-8.9751 -71.69966,-22.2012 -105.59961,-22.2012 -38.49993,0 -88.40045,11.4317 -119.900391,21.3516 C 76.799621,449.96896 64,442.03166 64,430.78906 V 80.94726 C 64,69.51586 70.799631,58.93546 82.099609,54.87306 110.29956,44.57516 157.50009,32.00977 208,32.00977 Z m 288,0 c 50.49991,0 97.70044,12.56619 125.90039,22.76949 C 633.20037,58.93616 640,69.51586 640,80.94726 v 349.8418 c 0,11.2426 -12.79963,19.0854 -24.09961,15.5899 -31.49995,-9.9199 -81.40046,-21.3516 -119.90039,-21.3516 -33.89995,0 -78.99966,13.2261 -105.59961,22.2012 C 379.60041,450.81856 368,443.35616 368,432.58596 V 76.03516 c 0,-3.968 1.60001,-7.9362 5,-10.3926 19.59997,-14.7381 64.6001,-33.63279 123,-33.63279 z M 335.52734,45.75386 c -0.1289,0.093 -0.23137,0.2032 -0.35937,0.2969 -0.198,0.1477 -0.428,0.2796 -0.625,0.4278 z m 33.67969,0.5372 0.24805,0.1875 c -0.0427,-0.033 -0.0937,-0.061 -0.13672,-0.094 -0.0393,-0.03 -0.0713,-0.064 -0.11133,-0.094 z" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#000000;fill-opacity:1;stroke-width:1;-inkscape-stroke:none"
|
||||||
|
d="m 206.76992,184 c -36.98368,0 -73.07301,9.2343 -94.76923,16.9066 v 185.1887 c 27.62799,-7.7405 62.70503,-15.0804 94.76923,-15.0804 28.33376,0 58.16312,7.6425 81.23077,14.806 V 203.0154 C 273.60322,195.1776 243.44241,184 206.76992,184 Z"
|
||||||
|
id="path4372-8"
|
||||||
|
sodipodi:nodetypes="sccsccs" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:63.9999;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 255.99995,122.53007 c -31.8285,-15.342 -80.43462,-15.4137 -112,0"
|
||||||
|
id="path2371-6"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
54
client/icons/customIcons.less
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
.fac {
|
||||||
|
display : inline-block;
|
||||||
|
}
|
||||||
|
.position-top-left {
|
||||||
|
content: url('../icons/position-top-left.svg');
|
||||||
|
}
|
||||||
|
.position-top-right {
|
||||||
|
content: url('../icons/position-top-right.svg');
|
||||||
|
}
|
||||||
|
.position-bottom-left {
|
||||||
|
content: url('../icons/position-bottom-left.svg');
|
||||||
|
}
|
||||||
|
.position-bottom-right {
|
||||||
|
content: url('../icons/position-bottom-right.svg');
|
||||||
|
}
|
||||||
|
.position-top {
|
||||||
|
content: url('../icons/position-top.svg');
|
||||||
|
}
|
||||||
|
.position-right {
|
||||||
|
content: url('../icons/position-right.svg');
|
||||||
|
}
|
||||||
|
.position-bottom {
|
||||||
|
content: url('../icons/position-bottom.svg');
|
||||||
|
}
|
||||||
|
.position-left {
|
||||||
|
content: url('../icons/position-left.svg');
|
||||||
|
}
|
||||||
|
.mask-edge {
|
||||||
|
content: url('../icons/mask-edge.svg');
|
||||||
|
}
|
||||||
|
.mask-corner {
|
||||||
|
content: url('../icons/mask-corner.svg');
|
||||||
|
}
|
||||||
|
.mask-center {
|
||||||
|
content: url('../icons/mask-center.svg');
|
||||||
|
}
|
||||||
|
.fa-file-c {
|
||||||
|
content: url('../icons/fa-file-c.svg');
|
||||||
|
}
|
||||||
|
.book-front-cover {
|
||||||
|
content: url('../icons/book-front-cover.svg');
|
||||||
|
}
|
||||||
|
.book-inside-cover {
|
||||||
|
content: url('../icons/book-inside-cover.svg');
|
||||||
|
}
|
||||||
|
.davek {
|
||||||
|
content: url('../icons/Davek.svg');
|
||||||
|
}
|
||||||
|
.rellanic {
|
||||||
|
content: url('../icons/Rellanic.svg');
|
||||||
|
}
|
||||||
|
.iokharic {
|
||||||
|
content: url('../icons/Iokharic.svg');
|
||||||
|
}
|
||||||
30
client/icons/fa-file-c.svg
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 610.4 816.5" style="enable-background:new 0 0 610.4 816.5;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:20;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<title>fa-file-c</title>
|
||||||
|
<g id="Layer_2_1_">
|
||||||
|
<g id="Layer_1-2">
|
||||||
|
<g id="page">
|
||||||
|
<path id="page-2" d="M610.3,468.3c0,77.3,0.2,154.5,0,231.8s-39.8,116.5-116.8,116.4c-127.6,0-255.1,0-382.7,0
|
||||||
|
c-68.1,0-110.5-41.7-110.6-109.8c-0.2-197.7-0.2-395.5,0-593.2c0-68.4,43.2-110.9,112.1-111c90-0.1,180,0.2,270-0.2
|
||||||
|
c12.8,0,21.5,0.6,32.9,4c17.1,5,152.7,150.7,190.7,188.8c-0.7,18-6,5.7,1.4,35.1c0,6.8,3.1,11.2,3.1,18.1
|
||||||
|
C610.2,320.8,610.3,395.7,610.3,468.3z"/>
|
||||||
|
<path id="white_corner" class="st0" d="M364.1,0v200c0,9.3,1.7,25.6,13.1,36.8c12,11.7,28.8,12.1,37.5,12.2
|
||||||
|
c119.8,1.3,195.6,0.4,195.6,0.4l0,0l-0.3-54.3l-197,1l3-192L364.1,0z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st1" d="M317.7,719.8c-38.3,0-71-8.1-98.3-24.3c-27.2-16.2-48.1-39.2-62.7-69C142.3,596.8,135,561.2,135,520
|
||||||
|
c0-30.9,4.1-58.6,12.4-83.1c8.3-24.5,20.2-45.3,35.9-62.4c15.6-17.1,34.9-30.4,57.7-39.8s48.4-14.1,76.7-14.1
|
||||||
|
c22.1-0.1,44,3.1,65.1,9.7c20.6,6.4,38.4,15.9,53.5,28.4c4.8,3.7,8,7.8,9.7,12.4c1.6,4.2,1.8,8.9,0.6,13.2
|
||||||
|
c-1.2,4.1-3.5,7.7-6.6,10.5c-3.1,2.8-7.2,4.2-11.3,4.1c-4.4,0-9.4-1.8-14.9-5.5c-13-10.5-27.7-18.6-43.6-23.7
|
||||||
|
c-16.6-5.3-33.9-7.9-51.3-7.7c-29.1,0-53.7,6.2-74,18.5s-35.5,30.3-45.8,53.8c-10.3,23.6-15.4,52.1-15.4,85.5s5.1,62.1,15.4,85.9
|
||||||
|
c10.3,23.7,25.6,41.8,45.8,54.1c20.2,12.3,44.9,18.5,74,18.5c17.4,0.1,34.8-2.6,51.3-8c16.2-5.3,31.3-13.5,44.7-24
|
||||||
|
c5.5-3.7,10.5-5.4,14.9-5.3c4,0.1,7.9,1.5,11,4.1c3,2.7,5.2,6.1,6.4,9.9c1.3,4.1,1.3,8.6,0,12.7c-1.3,4.4-4.1,8.3-8.6,11.6
|
||||||
|
c-15.5,13.3-33.6,23.3-54.4,30.1C362.7,716.6,340.3,720,317.7,719.8z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
63
client/icons/mask-center.svg
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="mask-center.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139"><pattern
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#Strips1_1"
|
||||||
|
id="pattern3077"
|
||||||
|
patternTransform="matrix(23.13193,-23.131931,19.25517,19.25517,18.091544,-20.306833)" /><pattern
|
||||||
|
inkscape:collect="always"
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
width="2"
|
||||||
|
height="1"
|
||||||
|
patternTransform="translate(0,0) scale(10,10)"
|
||||||
|
id="Strips1_1"
|
||||||
|
inkscape:stockid="Stripes 1:1"><rect
|
||||||
|
style="fill:black;stroke:none"
|
||||||
|
x="0"
|
||||||
|
y="-0.5"
|
||||||
|
width="1"
|
||||||
|
height="2"
|
||||||
|
id="rect2097" /></pattern></defs><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.67711183"
|
||||||
|
inkscape:cx="31.75251"
|
||||||
|
inkscape:cy="260.66595"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="M 48,-5.2e-6 C 21.40803,-5.2e-6 1.98e-5,21.408025 1.98e-5,47.999995 V 464 C 1.98e-5,490.59197 21.40803,512 48,512 h 352 c 26.59198,0 48,-21.40803 48,-48 V 47.999995 C 448,21.408025 426.59198,-5.2e-6 400,-5.2e-6 Z M 64,63.999995 H 384 V 448 H 64 Z" /><rect
|
||||||
|
style="fill:url(#pattern3077);fill-opacity:1;stroke:#000000;stroke-width:48;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="176"
|
||||||
|
height="240"
|
||||||
|
x="136.00002"
|
||||||
|
y="136"
|
||||||
|
rx="48"
|
||||||
|
ry="48" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
63
client/icons/mask-corner.svg
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="mask-corner.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139"><pattern
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#Strips1_1"
|
||||||
|
id="pattern3077"
|
||||||
|
patternTransform="matrix(23.131931,-23.131931,19.25517,19.25517,26.214281,-26.952711)" /><pattern
|
||||||
|
inkscape:collect="always"
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
width="2"
|
||||||
|
height="1"
|
||||||
|
patternTransform="translate(0,0) scale(10,10)"
|
||||||
|
id="Strips1_1"
|
||||||
|
inkscape:stockid="Stripes 1:1"><rect
|
||||||
|
style="fill:black;stroke:none"
|
||||||
|
x="0"
|
||||||
|
y="-0.5"
|
||||||
|
width="1"
|
||||||
|
height="2"
|
||||||
|
id="rect2097" /></pattern></defs><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.95758074"
|
||||||
|
inkscape:cx="275.17262"
|
||||||
|
inkscape:cy="306.50157"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="M 48,-5.2e-6 C 21.40803,-5.2e-6 1.98e-5,21.408025 1.98e-5,47.999995 V 464 C 1.98e-5,490.59197 21.40803,512 48,512 h 352 c 26.59198,0 48,-21.40803 48,-48 V 47.999995 C 448,21.408025 426.59198,-5.2e-6 400,-5.2e-6 Z M 64,63.999995 H 384 V 448 H 64 Z" /><rect
|
||||||
|
style="fill:url(#pattern3077);fill-opacity:1;stroke:#000000;stroke-width:48;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="208"
|
||||||
|
height="240"
|
||||||
|
x="32.000011"
|
||||||
|
y="32.000011"
|
||||||
|
rx="48"
|
||||||
|
ry="48" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
69
client/icons/mask-edge.svg
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="mask-edge.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139"><pattern
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#Strips1_1"
|
||||||
|
id="pattern3077"
|
||||||
|
patternTransform="matrix(23.131931,-23.13193,19.25517,19.25517,26.214281,-26.952711)" /><pattern
|
||||||
|
inkscape:collect="always"
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
width="2"
|
||||||
|
height="1"
|
||||||
|
patternTransform="translate(0,0) scale(10,10)"
|
||||||
|
id="Strips1_1"
|
||||||
|
inkscape:stockid="Stripes 1:1"><rect
|
||||||
|
style="fill:black;stroke:none"
|
||||||
|
x="0"
|
||||||
|
y="-0.5"
|
||||||
|
width="1"
|
||||||
|
height="2"
|
||||||
|
id="rect2097" /></pattern></defs><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.95758074"
|
||||||
|
inkscape:cx="231.31209"
|
||||||
|
inkscape:cy="171.78708"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="M 48,-5.2e-6 C 21.40803,-5.2e-6 1.98e-5,21.408025 1.98e-5,47.999995 V 464 C 1.98e-5,490.59197 21.40803,512 48,512 h 352 c 26.59198,0 48,-21.40803 48,-48 V 47.999995 C 448,21.408025 426.59198,-5.2e-6 400,-5.2e-6 Z M 64,63.999995 H 384 V 448 H 64 Z" /><rect
|
||||||
|
style="fill:url(#pattern3077);fill-opacity:1;stroke:#000000;stroke-width:48;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="208"
|
||||||
|
height="447.99997"
|
||||||
|
x="32.000011"
|
||||||
|
y="32.000011"
|
||||||
|
rx="48"
|
||||||
|
ry="48" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke-width:47.9999;stroke-linejoin:round;stroke-dasharray:none;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect4640"
|
||||||
|
width="48"
|
||||||
|
height="512"
|
||||||
|
x="216"
|
||||||
|
y="0" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
46
client/icons/position-bottom-left.svg
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-bottom-left.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.70792086"
|
||||||
|
inkscape:cx="174.45453"
|
||||||
|
inkscape:cy="325.60137"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="m 48,511.99998 c -26.59197,0 -48.00000035682677,-21.40803 -48.00000035682677,-48 v -416 C -3.5682677e-7,21.40801 21.40803,-1.9692461e-5 48,-1.9692461e-5 h 352 c 26.59198,0 48,21.408029692461 48,47.999999692461 v 416 c 0,26.59197 -21.40802,48 -48,48 z m 16,-64 h 320 v -384 H 64 Z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="208"
|
||||||
|
height="240"
|
||||||
|
x="-3.5682677e-07"
|
||||||
|
y="-512"
|
||||||
|
rx="48"
|
||||||
|
ry="48"
|
||||||
|
transform="scale(1,-1)" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
46
client/icons/position-bottom-right.svg
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-bottom-right.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.70792086"
|
||||||
|
inkscape:cx="174.45453"
|
||||||
|
inkscape:cy="325.60137"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="m 400,511.99998 c 26.59197,0 48,-21.40803 48,-48 v -416 C 448,21.40801 426.59197,-1.9692461e-5 400,-1.9692461e-5 H 48 C 21.40802,-1.9692461e-5 -3.5682677e-7,21.40801 -3.5682677e-7,47.99998 v 416 c 0,26.59197 21.40802035682677,48 48.00000035682677,48 z m -16,-64 H 64 v -384 h 320 z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="208"
|
||||||
|
height="240"
|
||||||
|
x="-448"
|
||||||
|
y="-512"
|
||||||
|
rx="48"
|
||||||
|
ry="48"
|
||||||
|
transform="scale(-1)" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
46
client/icons/position-bottom.svg
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-bottom.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="1.0011513"
|
||||||
|
inkscape:cx="273.18549"
|
||||||
|
inkscape:cy="216.25103"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201-2"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="m 48,512.00004 c -26.5919,0 -48,-21.4081 -48,-48 V 47.999996 C 0,21.408026 21.4081,-3.8146973e-6 48,-3.8146973e-6 h 352 c 26.592,0 48,21.4080298146973 48,47.9999998146973 V 464.00004 c 0,26.5919 -21.408,48 -48,48 z m 16,-64 H 384 V 63.999996 H 64 Z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30.0001;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206-8"
|
||||||
|
width="447.99997"
|
||||||
|
height="240"
|
||||||
|
x="1.40625e-05"
|
||||||
|
y="-512.00006"
|
||||||
|
rx="48"
|
||||||
|
ry="48"
|
||||||
|
transform="scale(1,-1)" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
45
client/icons/position-left.svg
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-left.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.70792086"
|
||||||
|
inkscape:cx="164.56642"
|
||||||
|
inkscape:cy="243.6713"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201-0"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="M 48,0 C 21.4081,0 0,21.40803 0,48 v 416 c 0,26.59197 21.4081,48 48,48 h 352.0001 c 26.5919,0 48,-21.40803 48,-48 V 48 c 0,-26.59197 -21.4081,-48 -48,-48 z M 64,64 H 384.0001 V 448 H 64 Z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206-2"
|
||||||
|
width="208"
|
||||||
|
height="512.00006"
|
||||||
|
x="7.0762391e-05"
|
||||||
|
y="-8.8710935e-05"
|
||||||
|
rx="48"
|
||||||
|
ry="48.000004" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
46
client/icons/position-right.svg
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-right.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.70792086"
|
||||||
|
inkscape:cx="164.56642"
|
||||||
|
inkscape:cy="243.6713"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201-0"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="m 400.0001,0 c 26.5919,0 48,21.40803 48,48 v 416 c 0,26.59197 -21.4081,48 -48,48 H 48 C 21.4081,512 0,490.59197 0,464 V 48 C 0,21.40803 21.4081,0 48,0 Z m -16,64 H 64 v 384 h 320.0001 z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206-2"
|
||||||
|
width="208"
|
||||||
|
height="512.00006"
|
||||||
|
x="-448.00003"
|
||||||
|
y="-8.8710935e-05"
|
||||||
|
rx="48"
|
||||||
|
ry="48.000004"
|
||||||
|
transform="scale(-1,1)" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
45
client/icons/position-top-left.svg
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-top-left.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.70792086"
|
||||||
|
inkscape:cx="174.45453"
|
||||||
|
inkscape:cy="325.60137"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="M 48,0 C 21.40803,0 0,21.40803 0,48 v 416 c 0,26.59197 21.40803,48 48,48 h 352 c 26.59198,0 48,-21.40803 48,-48 V 48 C 448,21.40803 426.59198,0 400,0 Z M 64,64 H 384 V 448 H 64 Z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="208"
|
||||||
|
height="240"
|
||||||
|
x="-3.5682677e-07"
|
||||||
|
y="-1.9692461e-05"
|
||||||
|
rx="48"
|
||||||
|
ry="48" /></svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
46
client/icons/position-top-right.svg
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-top-right.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="0.70792086"
|
||||||
|
inkscape:cx="174.45453"
|
||||||
|
inkscape:cy="325.60137"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="m 400,0 c 26.59197,0 48,21.40803 48,48 v 416 c 0,26.59197 -21.40803,48 -48,48 H 48 C 21.40802,512 -3.5682677e-7,490.59197 -3.5682677e-7,464 V 48 C -3.5682677e-7,21.40803 21.40802,0 48,0 Z M 384,64 H 64 v 384 h 320 z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206"
|
||||||
|
width="208"
|
||||||
|
height="240"
|
||||||
|
x="-448"
|
||||||
|
y="-1.9692461e-05"
|
||||||
|
rx="48"
|
||||||
|
ry="48"
|
||||||
|
transform="scale(-1,1)" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
45
client/icons/position-top.svg
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg135"
|
||||||
|
sodipodi:docname="position-top.svg"
|
||||||
|
width="448"
|
||||||
|
height="512"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs139" /><sodipodi:namedview
|
||||||
|
id="namedview137"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="1.0011513"
|
||||||
|
inkscape:cx="273.18549"
|
||||||
|
inkscape:cy="216.25103"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="991"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||||
|
id="rect12201-2"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
d="M 48,0 C 21.4081,0 0,21.4081 0,48 v 416.00004 c 0,26.59197 21.4081,48 48,48 h 352 c 26.592,0 48,-21.40803 48,-48 V 48 C 448,21.4081 426.592,0 400,0 Z M 64,64 H 384 V 448.00004 H 64 Z" /><rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30.0001;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||||
|
id="rect12206-8"
|
||||||
|
width="447.99997"
|
||||||
|
height="240"
|
||||||
|
x="1.40625e-05"
|
||||||
|
y="-3.8146973e-06"
|
||||||
|
rx="48"
|
||||||
|
ry="48" /></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
43
install/README.WINDOWS.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Windows Installation Instructions
|
||||||
|
|
||||||
|
## Before Installing
|
||||||
|
|
||||||
|
These instructions assume that you are installing to a completely new, fresh Windows 10 installation. As such, some steps may not be necessary if you are installing to an existing Windows 10 instance.
|
||||||
|
|
||||||
|
## Installation instructions
|
||||||
|
|
||||||
|
1. Download the installation script from https://raw.githubusercontent.com/naturalcrit/homebrewery/master/install/windows/install.ps1.
|
||||||
|
|
||||||
|
2. Run Powershell as an Administrator.
|
||||||
|
a. Click the Start menu or press the Windows key.
|
||||||
|
b. Type `powershell` into the Search box.
|
||||||
|
c. Right click on the Powershell app and select "Run As Administrator".
|
||||||
|
d. Click YES in the prompt that appears.
|
||||||
|
|
||||||
|
3. Change the script execution policy.
|
||||||
|
a. Run the Powershell command `Set-ExecutionPolicy Bypass -Scope Process`.
|
||||||
|
b. Allow the change to be made - press Y at the prompt that appears.
|
||||||
|
|
||||||
|
4. Run the installation script.
|
||||||
|
a. Navigate to the location of the script, e.g. `cd C:\Users\ExampleUser\Downloads`.
|
||||||
|
b. Start the script - `.\install.ps1`
|
||||||
|
|
||||||
|
5. Once the script has completed, it will start the Homebrewery server. This will normally cause a Network Access prompt for NodeJS - if this appears, click "Allow".
|
||||||
|
|
||||||
|
**NOTE:** At this time, the script **ONLY** installs HomeBrewery. It does **NOT** install the NaturalCrit login system, as that is currently a completely separate project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
These installation instructions have been tested on the following Ubuntu releases:
|
||||||
|
|
||||||
|
- *Windows 10 Home - OS Build 19045.2546*
|
||||||
|
|
||||||
|
## Final Notes
|
||||||
|
|
||||||
|
While this installation process works successfully at the time of writing (January 23, 2023), it relies on all of the Node.JS packages used in the HomeBrewery project retaining their cross-platform capabilities to continue to function. This is one of the inherent advantages of Node.JS, but it is by no means guaranteed and as such, functionality or even installation may fail without warning at some point in the future.
|
||||||
|
|
||||||
|
Regards,
|
||||||
|
G
|
||||||
|
January 23, 2023
|
||||||
51
install/windows/install.ps1
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
Write-Host Homebrewery Install -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
Write-Host =================== -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
Write-Host Install Chocolatey -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
Write-Host Instructions from https://chocolate.org/install -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||||
|
|
||||||
|
Write-Host Install Node JS v16.11.1 -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
choco install nodejs --version=16.11.1 -y
|
||||||
|
|
||||||
|
Write-Host Install MongoDB v 4.4.4 -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
choco install mongodb --version=4.4.4 -y
|
||||||
|
|
||||||
|
Write-Host Install GIT -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
choco install git -y
|
||||||
|
|
||||||
|
Write-Host Refresh Environment -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||||
|
Update-SessionEnvironment
|
||||||
|
|
||||||
|
Write-Host Create Homebrewery directory - C:\Homebrewery -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
mkdir C:\Hombrewery
|
||||||
|
cd C:\Hombrewery
|
||||||
|
|
||||||
|
Write-Host Download Homebrewery project files -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
git clone https://github.com/naturalcrit/homebrewery.git
|
||||||
|
|
||||||
|
Write-Host Install Homebrewery files -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
cd homebrewery
|
||||||
|
|
||||||
|
npm install
|
||||||
|
npm audit fix
|
||||||
|
|
||||||
|
Write-Host Set install type to 'local' -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
[System.Environment]::SetEnvironmentVariable('NODE_ENV', 'local')
|
||||||
|
|
||||||
|
Write-Host INSTALL COMPLETE -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
Write-Host To start Homebrewery in the future, open a terminal in the Homebrewery directory and run npm start -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
Write-Host ================================================================================================== -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
Write-Host Start Homebrewery -BackgroundColor Black -ForegroundColor Yellow
|
||||||
|
|
||||||
|
npm start
|
||||||
18153
package-lock.json
generated
81
package.json
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||||
"version": "3.6.0",
|
"version": "3.7.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "16.11.x"
|
"node": "16.13.x"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -12,23 +12,25 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev.js",
|
"dev": "node scripts/dev.js",
|
||||||
"quick": "node scripts/quick.js",
|
"quick": "node scripts/quick.js",
|
||||||
"build": "node scripts/buildHomebrew.js",
|
"build": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
||||||
"buildall": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
|
||||||
"builddev": "node scripts/buildHomebrew.js --dev",
|
"builddev": "node scripts/buildHomebrew.js --dev",
|
||||||
"lint": "eslint --fix **/*.{js,jsx}",
|
"lint": "eslint --fix **/*.{js,jsx}",
|
||||||
"lint:dry": "eslint **/*.{js,jsx}",
|
"lint:dry": "eslint **/*.{js,jsx}",
|
||||||
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
||||||
"verify": "npm run lint && npm test",
|
"verify": "npm run lint && npm test",
|
||||||
"test": "jest",
|
"test": "jest --runInBand",
|
||||||
"test:api-unit": "jest server/*.spec.js --verbose",
|
"test:api-unit": "jest server/*.spec.js --verbose",
|
||||||
"test:coverage": "jest --coverage --silent",
|
"test:coverage": "jest --coverage --silent --runInBand",
|
||||||
"test:dev": "jest --verbose --watch",
|
"test:dev": "jest --verbose --watch",
|
||||||
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
||||||
"test:mustache-span": "jest tests/markdown/mustache-span.test.js --verbose",
|
"test:mustache-syntax": "jest '.*(mustache-syntax).*' --verbose --noStackTrace",
|
||||||
|
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
|
||||||
|
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
|
||||||
|
"test:mustache-syntax:injection": "jest '.*(mustache-syntax).*' -t '^Injection:.*' --verbose --noStackTrace",
|
||||||
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
||||||
"phb": "node scripts/phb.js",
|
"phb": "node scripts/phb.js",
|
||||||
"prod": "set NODE_ENV=production && npm run build",
|
"prod": "set NODE_ENV=production && npm run build",
|
||||||
"postinstall": "npm run buildall",
|
"postinstall": "npm run build",
|
||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"author": "stolksdorf",
|
"author": "stolksdorf",
|
||||||
@@ -37,26 +39,30 @@
|
|||||||
"build/*"
|
"build/*"
|
||||||
],
|
],
|
||||||
"jest": {
|
"jest": {
|
||||||
"testTimeout": 15000,
|
"testTimeout": 30000,
|
||||||
"modulePaths": [
|
"modulePaths": [
|
||||||
"mode_modules",
|
"node_modules",
|
||||||
"shared",
|
"shared",
|
||||||
"server"
|
"server"
|
||||||
],
|
],
|
||||||
"coverageThreshold" : {
|
"coveragePathIgnorePatterns": [
|
||||||
"global" : {
|
"build/*"
|
||||||
"statements" : 25,
|
],
|
||||||
"branches" : 10,
|
"coverageThreshold": {
|
||||||
"functions" : 22,
|
"global": {
|
||||||
"lines" : 25
|
"statements": 25,
|
||||||
|
"branches": 10,
|
||||||
|
"functions": 22,
|
||||||
|
"lines": 25
|
||||||
},
|
},
|
||||||
"server/homebrew.api.js" : {
|
"server/homebrew.api.js": {
|
||||||
"statements" : 65,
|
"statements": 65,
|
||||||
"branches" : 50,
|
"branches": 50,
|
||||||
"functions" : 60,
|
"functions": 60,
|
||||||
"lines" : 70
|
"lines": 70
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"setupFilesAfterEnv": ["jest-expect-message"]
|
||||||
},
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"presets": [
|
"presets": [
|
||||||
@@ -68,45 +74,46 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.20.12",
|
"@babel/core": "^7.21.4",
|
||||||
"@babel/plugin-transform-runtime": "^7.19.6",
|
"@babel/plugin-transform-runtime": "^7.21.4",
|
||||||
"@babel/preset-env": "^7.19.4",
|
"@babel/preset-env": "^7.21.4",
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@babel/preset-react": "^7.18.6",
|
||||||
"body-parser": "^1.20.1",
|
"@googleapis/drive": "^5.0.2",
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"codemirror": "^5.65.6",
|
"codemirror": "^5.65.6",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"create-react-class": "^15.7.0",
|
"create-react-class": "^15.7.0",
|
||||||
"dedent-tabs": "^0.10.2",
|
"dedent-tabs": "^0.10.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"express-static-gzip": "2.1.7",
|
"express-static-gzip": "2.1.7",
|
||||||
"fs-extra": "11.1.0",
|
"fs-extra": "11.1.1",
|
||||||
"googleapis": "110.0.0",
|
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jwt-simple": "^0.5.6",
|
"jwt-simple": "^0.5.6",
|
||||||
"less": "^3.13.1",
|
"less": "^3.13.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "4.2.12",
|
"marked": "4.3.0",
|
||||||
"marked-extended-tables": "^1.0.5",
|
"marked-extended-tables": "^1.0.5",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mongoose": "^6.8.4",
|
"mongoose": "^7.0.3",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.0",
|
"nconf": "^0.12.0",
|
||||||
"npm": "^8.10.0",
|
"npm": "^9.6.4",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-frame-component": "4.1.3",
|
"react-frame-component": "5.2.6",
|
||||||
"react-router-dom": "6.7.0",
|
"react-router-dom": "6.10.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^6.1.0",
|
"superagent": "^6.1.0",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.37.0",
|
||||||
"eslint-plugin-react": "^7.32.1",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"jest": "^29.4.0",
|
"jest": "^29.5.0",
|
||||||
|
"jest-expect-message": "^1.1.3",
|
||||||
"supertest": "^6.3.3"
|
"supertest": "^6.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const transforms = {
|
|||||||
|
|
||||||
const build = async ({ bundle, render, ssr })=>{
|
const build = async ({ bundle, render, ssr })=>{
|
||||||
const css = await lessTransform.generate({ paths: './shared' });
|
const css = await lessTransform.generate({ paths: './shared' });
|
||||||
|
//css = `@layer bundle {\n${css}\n}`;
|
||||||
await fs.outputFile('./build/homebrew/bundle.css', css);
|
await fs.outputFile('./build/homebrew/bundle.css', css);
|
||||||
await fs.outputFile('./build/homebrew/bundle.js', bundle);
|
await fs.outputFile('./build/homebrew/bundle.js', bundle);
|
||||||
await fs.outputFile('./build/homebrew/ssr.js', ssr);
|
await fs.outputFile('./build/homebrew/ssr.js', ssr);
|
||||||
@@ -72,6 +73,7 @@ fs.emptyDirSync('./build');
|
|||||||
themeData.path = dir;
|
themeData.path = dir;
|
||||||
themes.V3[dir] = (themeData);
|
themes.V3[dir] = (themeData);
|
||||||
fs.copy(`./themes/V3/${dir}/dropdownTexture.png`, `./build/themes/V3/${dir}/dropdownTexture.png`);
|
fs.copy(`./themes/V3/${dir}/dropdownTexture.png`, `./build/themes/V3/${dir}/dropdownTexture.png`);
|
||||||
|
fs.copy(`./themes/V3/${dir}/dropdownPreview.png`, `./build/themes/V3/${dir}/dropdownPreview.png`);
|
||||||
const src = `./themes/V3/${dir}/style.less`;
|
const src = `./themes/V3/${dir}/style.less`;
|
||||||
((outputDirectory)=>{
|
((outputDirectory)=>{
|
||||||
less.render(fs.readFileSync(src).toString(), {
|
less.render(fs.readFileSync(src).toString(), {
|
||||||
@@ -95,6 +97,7 @@ fs.emptyDirSync('./build');
|
|||||||
// Move assets
|
// Move assets
|
||||||
await fs.copy('./themes/fonts', './build/fonts');
|
await fs.copy('./themes/fonts', './build/fonts');
|
||||||
await fs.copy('./themes/assets', './build/assets');
|
await fs.copy('./themes/assets', './build/assets');
|
||||||
|
await fs.copy('./client/icons', './build/icons');
|
||||||
|
|
||||||
//v==----------------------------- BUNDLE PACKAGES --------------------------------==v//
|
//v==----------------------------- BUNDLE PACKAGES --------------------------------==v//
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const mw = {
|
|||||||
.status(401)
|
.status(401)
|
||||||
.send('Authorization Required');
|
.send('Authorization Required');
|
||||||
}
|
}
|
||||||
const [username, password] = new Buffer(req.get('authorization').split(' ').pop(), 'base64')
|
const [username, password] = Buffer.from(req.get('authorization').split(' ').pop(), 'base64')
|
||||||
.toString('ascii')
|
.toString('ascii')
|
||||||
.split(':');
|
.split(':');
|
||||||
if(process.env.ADMIN_USER === username && process.env.ADMIN_PASS === password){
|
if(process.env.ADMIN_USER === username && process.env.ADMIN_PASS === password){
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const splitTextStyleAndMetadata = (brew)=>{
|
|||||||
const index = brew.text.indexOf('```\n\n');
|
const index = brew.text.indexOf('```\n\n');
|
||||||
const metadataSection = brew.text.slice(12, index - 1);
|
const metadataSection = brew.text.slice(12, index - 1);
|
||||||
const metadata = yaml.load(metadataSection);
|
const metadata = yaml.load(metadataSection);
|
||||||
Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme']));
|
Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang']));
|
||||||
brew.text = brew.text.slice(index + 5);
|
brew.text = brew.text.slice(index + 5);
|
||||||
}
|
}
|
||||||
if(brew.text.startsWith('```css')) {
|
if(brew.text.startsWith('```css')) {
|
||||||
@@ -224,6 +224,7 @@ app.get('/user/:username', async (req, res, next)=>{
|
|||||||
'pageCount',
|
'pageCount',
|
||||||
'description',
|
'description',
|
||||||
'authors',
|
'authors',
|
||||||
|
'lang',
|
||||||
'published',
|
'published',
|
||||||
'views',
|
'views',
|
||||||
'shareId',
|
'shareId',
|
||||||
@@ -293,8 +294,15 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{
|
|||||||
app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
|
app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
|
||||||
sanitizeBrew(req.brew, 'share');
|
sanitizeBrew(req.brew, 'share');
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
req.brew.views = 0;
|
const brew = {
|
||||||
req.brew.title = `CLONE - ${req.brew.title}`;
|
shareId : req.brew.shareId,
|
||||||
|
title : `CLONE - ${req.brew.title}`,
|
||||||
|
text : req.brew.text,
|
||||||
|
style : req.brew.style,
|
||||||
|
renderer : req.brew.renderer,
|
||||||
|
theme : req.brew.theme
|
||||||
|
};
|
||||||
|
req.brew = _.defaults(brew, DEFAULT_BREW);
|
||||||
|
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
title : 'New',
|
title : 'New',
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ const DEFAULT_BREW = {
|
|||||||
authors : [],
|
authors : [],
|
||||||
tags : [],
|
tags : [],
|
||||||
systems : [],
|
systems : [],
|
||||||
|
lang : 'en',
|
||||||
thumbnail : '',
|
thumbnail : '',
|
||||||
|
views : 0,
|
||||||
published : false,
|
published : false,
|
||||||
pageCount : 1,
|
pageCount : 1,
|
||||||
gDrive : false,
|
gDrive : false,
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ const disconnect = async ()=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const connect = async (config)=>{
|
const connect = async (config)=>{
|
||||||
return await Mongoose.connect(getMongoDBURL(config),
|
return await Mongoose.connect(getMongoDBURL(config), { retryWrites: false })
|
||||||
{ retryWrites: false }, handleConnectionError);
|
.catch((error)=>handleConnectionError(error));
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { google } = require('googleapis');
|
const googleDrive = require('@googleapis/drive');
|
||||||
const { nanoid } = require('nanoid');
|
const { nanoid } = require('nanoid');
|
||||||
const token = require('./token.js');
|
const token = require('./token.js');
|
||||||
const config = require('./config.js');
|
const config = require('./config.js');
|
||||||
@@ -14,7 +14,7 @@ if(!config.get('service_account')){
|
|||||||
config.get('service_account');
|
config.get('service_account');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serviceAuth = google.auth.fromJSON(keys);
|
serviceAuth = googleDrive.auth.fromJSON(keys);
|
||||||
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
@@ -22,7 +22,7 @@ if(!config.get('service_account')){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
google.options({ auth: serviceAuth || config.get('google_api_key') });
|
const defaultAuth = serviceAuth || config.get('google_api_key');
|
||||||
|
|
||||||
const GoogleActions = {
|
const GoogleActions = {
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ const GoogleActions = {
|
|||||||
throw (err);
|
throw (err);
|
||||||
}
|
}
|
||||||
|
|
||||||
const oAuth2Client = new google.auth.OAuth2(
|
const oAuth2Client = new googleDrive.auth.OAuth2(
|
||||||
config.get('google_client_id'),
|
config.get('google_client_id'),
|
||||||
config.get('google_client_secret'),
|
config.get('google_client_secret'),
|
||||||
'/auth/google/redirect'
|
'/auth/google/redirect'
|
||||||
@@ -60,7 +60,7 @@ const GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getGoogleFolder : async (auth)=>{
|
getGoogleFolder : async (auth)=>{
|
||||||
const drive = google.drive({ version: 'v3', auth });
|
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||||
|
|
||||||
fileMetadata = {
|
fileMetadata = {
|
||||||
'name' : 'Homebrewery',
|
'name' : 'Homebrewery',
|
||||||
@@ -97,7 +97,7 @@ const GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
listGoogleBrews : async (auth)=>{
|
listGoogleBrews : async (auth)=>{
|
||||||
const drive = google.drive({ version: 'v3', auth });
|
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||||
|
|
||||||
const obj = await drive.files.list({
|
const obj = await drive.files.list({
|
||||||
pageSize : 1000,
|
pageSize : 1000,
|
||||||
@@ -129,14 +129,16 @@ const GoogleActions = {
|
|||||||
description : file.description,
|
description : file.description,
|
||||||
views : parseInt(file.properties.views),
|
views : parseInt(file.properties.views),
|
||||||
published : file.properties.published ? file.properties.published == 'true' : false,
|
published : file.properties.published ? file.properties.published == 'true' : false,
|
||||||
systems : []
|
systems : [],
|
||||||
|
lang : file.properties.lang,
|
||||||
|
thumbnail : file.properties.thumbnail
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return brews;
|
return brews;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateGoogleBrew : async (brew)=>{
|
updateGoogleBrew : async (brew)=>{
|
||||||
const drive = google.drive({ version: 'v3' });
|
const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth });
|
||||||
|
|
||||||
await drive.files.update({
|
await drive.files.update({
|
||||||
fileId : brew.googleId,
|
fileId : brew.googleId,
|
||||||
@@ -149,7 +151,8 @@ const GoogleActions = {
|
|||||||
editId : brew.editId || nanoid(12),
|
editId : brew.editId || nanoid(12),
|
||||||
pageCount : brew.pageCount,
|
pageCount : brew.pageCount,
|
||||||
renderer : brew.renderer || 'legacy',
|
renderer : brew.renderer || 'legacy',
|
||||||
isStubbed : true
|
isStubbed : true,
|
||||||
|
lang : brew.lang || 'en'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
media : {
|
media : {
|
||||||
@@ -167,7 +170,7 @@ const GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
newGoogleBrew : async (auth, brew)=>{
|
newGoogleBrew : async (auth, brew)=>{
|
||||||
const drive = google.drive({ version: 'v3', auth });
|
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||||
|
|
||||||
const media = {
|
const media = {
|
||||||
mimeType : 'text/plain',
|
mimeType : 'text/plain',
|
||||||
@@ -187,7 +190,8 @@ const GoogleActions = {
|
|||||||
pageCount : brew.pageCount,
|
pageCount : brew.pageCount,
|
||||||
renderer : brew.renderer || 'legacy',
|
renderer : brew.renderer || 'legacy',
|
||||||
isStubbed : true,
|
isStubbed : true,
|
||||||
version : 1
|
version : 1,
|
||||||
|
lang : brew.lang || 'en'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -218,7 +222,7 @@ const GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getGoogleBrew : async (id, accessId, accessType)=>{
|
getGoogleBrew : async (id, accessId, accessType)=>{
|
||||||
const drive = google.drive({ version: 'v3' });
|
const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth });
|
||||||
|
|
||||||
const obj = await drive.files.get({
|
const obj = await drive.files.get({
|
||||||
fileId : id,
|
fileId : id,
|
||||||
@@ -255,6 +259,7 @@ const GoogleActions = {
|
|||||||
description : obj.data.description,
|
description : obj.data.description,
|
||||||
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
|
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
|
||||||
authors : [],
|
authors : [],
|
||||||
|
lang : obj.data.properties.lang,
|
||||||
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
|
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
|
||||||
trashed : obj.data.trashed,
|
trashed : obj.data.trashed,
|
||||||
|
|
||||||
@@ -274,7 +279,7 @@ const GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteGoogleBrew : async (auth, id, accessId)=>{
|
deleteGoogleBrew : async (auth, id, accessId)=>{
|
||||||
const drive = google.drive({ version: 'v3', auth });
|
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||||
|
|
||||||
const obj = await drive.files.get({
|
const obj = await drive.files.get({
|
||||||
fileId : id,
|
fileId : id,
|
||||||
@@ -300,7 +305,7 @@ const GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
increaseView : async (id, accessId, accessType, brew)=>{
|
increaseView : async (id, accessId, accessType, brew)=>{
|
||||||
const drive = google.drive({ version: 'v3' });
|
const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth });
|
||||||
|
|
||||||
await drive.files.update({
|
await drive.files.update({
|
||||||
fileId : brew.googleId,
|
fileId : brew.googleId,
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
|
|
||||||
if(brew.authors.length === 0) {
|
if(brew.authors.length === 0) {
|
||||||
// Delete brew if there are no authors left
|
// Delete brew if there are no authors left
|
||||||
await brew.remove()
|
await HomebrewModel.deleteOne({ _id: brew._id })
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.error(err);
|
console.error(err);
|
||||||
throw { status: 500, message: 'Error while removing' };
|
throw { status: 500, message: 'Error while removing' };
|
||||||
@@ -317,8 +317,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
brew.textBin = zlib.deflateRawSync(brew.text);
|
brew.textBin = zlib.deflateRawSync(brew.text);
|
||||||
brew.text = undefined;
|
brew.text = undefined;
|
||||||
}
|
}
|
||||||
|
brew.markModified('authors'); //Mongo will not properly update arrays without markModified()
|
||||||
// Otherwise, save the brew with updated author list
|
|
||||||
await brew.save()
|
await brew.save()
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
throw { status: 500, message: err };
|
throw { status: 500, message: err };
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ describe('Tests for api', ()=>{
|
|||||||
|
|
||||||
let modelBrew;
|
let modelBrew;
|
||||||
let saveFunc;
|
let saveFunc;
|
||||||
let removeFunc;
|
let markModifiedFunc;
|
||||||
let saved;
|
let saved;
|
||||||
|
|
||||||
beforeEach(()=>{
|
beforeEach(()=>{
|
||||||
@@ -19,16 +19,16 @@ describe('Tests for api', ()=>{
|
|||||||
saved = { ...this, _id: '1' };
|
saved = { ...this, _id: '1' };
|
||||||
return saved;
|
return saved;
|
||||||
});
|
});
|
||||||
removeFunc = jest.fn(async function() {});
|
markModifiedFunc = jest.fn(()=>true);
|
||||||
|
|
||||||
modelBrew = (brew)=>({
|
modelBrew = (brew)=>({
|
||||||
...brew,
|
...brew,
|
||||||
save : saveFunc,
|
save : saveFunc,
|
||||||
remove : removeFunc,
|
markModified : markModifiedFunc,
|
||||||
toObject : function() {
|
toObject : function() {
|
||||||
delete this.save;
|
delete this.save;
|
||||||
delete this.toObject;
|
delete this.toObject;
|
||||||
delete this.remove;
|
delete this.markModified;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -58,6 +58,7 @@ describe('Tests for api', ()=>{
|
|||||||
description : 'this is a description',
|
description : 'this is a description',
|
||||||
tags : ['something', 'fun'],
|
tags : ['something', 'fun'],
|
||||||
systems : ['D&D 5e'],
|
systems : ['D&D 5e'],
|
||||||
|
lang : 'en',
|
||||||
renderer : 'v3',
|
renderer : 'v3',
|
||||||
theme : 'phb',
|
theme : 'phb',
|
||||||
published : true,
|
published : true,
|
||||||
@@ -71,7 +72,8 @@ describe('Tests for api', ()=>{
|
|||||||
lastViewed : new Date(),
|
lastViewed : new Date(),
|
||||||
version : 1,
|
version : 1,
|
||||||
pageCount : 1,
|
pageCount : 1,
|
||||||
textBin : ''
|
textBin : '',
|
||||||
|
views : 0
|
||||||
};
|
};
|
||||||
googleBrew = {
|
googleBrew = {
|
||||||
...hbBrew,
|
...hbBrew,
|
||||||
@@ -250,6 +252,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
pageCount : 1,
|
pageCount : 1,
|
||||||
published : false,
|
published : false,
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
|
lang : 'en',
|
||||||
shareId : undefined,
|
shareId : undefined,
|
||||||
systems : [],
|
systems : [],
|
||||||
tags : [],
|
tags : [],
|
||||||
@@ -261,7 +264,8 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
gDrive : false,
|
gDrive : false,
|
||||||
style : undefined,
|
style : undefined,
|
||||||
trashed : false,
|
trashed : false,
|
||||||
updatedAt : undefined
|
updatedAt : undefined,
|
||||||
|
views : 0
|
||||||
});
|
});
|
||||||
expect(next).toHaveBeenCalled();
|
expect(next).toHaveBeenCalled();
|
||||||
expect(api.getId).toHaveBeenCalledWith(req);
|
expect(api.getId).toHaveBeenCalledWith(req);
|
||||||
@@ -442,6 +446,7 @@ brew`);
|
|||||||
pageCount : 1,
|
pageCount : 1,
|
||||||
published : false,
|
published : false,
|
||||||
renderer : 'V3',
|
renderer : 'V3',
|
||||||
|
lang : 'en',
|
||||||
shareId : expect.any(String),
|
shareId : expect.any(String),
|
||||||
style : undefined,
|
style : undefined,
|
||||||
systems : [],
|
systems : [],
|
||||||
@@ -452,7 +457,8 @@ brew`);
|
|||||||
thumbnail : '',
|
thumbnail : '',
|
||||||
title : 'asdf',
|
title : 'asdf',
|
||||||
trashed : false,
|
trashed : false,
|
||||||
updatedAt : undefined
|
updatedAt : undefined,
|
||||||
|
views : 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -499,6 +505,7 @@ brew`);
|
|||||||
pageCount : undefined,
|
pageCount : undefined,
|
||||||
published : false,
|
published : false,
|
||||||
renderer : undefined,
|
renderer : undefined,
|
||||||
|
lang : 'en',
|
||||||
shareId : expect.any(String),
|
shareId : expect.any(String),
|
||||||
googleId : expect.any(String),
|
googleId : expect.any(String),
|
||||||
style : undefined,
|
style : undefined,
|
||||||
@@ -510,7 +517,8 @@ brew`);
|
|||||||
thumbnail : '',
|
thumbnail : '',
|
||||||
title : 'asdf',
|
title : 'asdf',
|
||||||
trashed : false,
|
trashed : false,
|
||||||
updatedAt : undefined
|
updatedAt : undefined,
|
||||||
|
views : 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -557,13 +565,14 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
const req = {};
|
const req = {};
|
||||||
|
|
||||||
await api.deleteBrew(req, res);
|
await api.deleteBrew(req, res);
|
||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).toHaveBeenCalled();
|
expect(model.deleteOne).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw on delete error', async ()=>{
|
it('should throw on delete error', async ()=>{
|
||||||
@@ -575,7 +584,7 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
removeFunc = jest.fn(async ()=>{ throw 'err'; });
|
model.deleteOne = jest.fn(async ()=>{ throw 'err'; });
|
||||||
const req = {};
|
const req = {};
|
||||||
|
|
||||||
let err;
|
let err;
|
||||||
@@ -588,7 +597,7 @@ brew`);
|
|||||||
expect(err).not.toBeUndefined();
|
expect(err).not.toBeUndefined();
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).toHaveBeenCalled();
|
expect(model.deleteOne).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete when one author', async ()=>{
|
it('should delete when one author', async ()=>{
|
||||||
@@ -600,13 +609,14 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
const req = { account: { username: 'test' } };
|
const req = { account: { username: 'test' } };
|
||||||
|
|
||||||
await api.deleteBrew(req, res);
|
await api.deleteBrew(req, res);
|
||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).toHaveBeenCalled();
|
expect(model.deleteOne).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove one author when multiple present', async ()=>{
|
it('should remove one author when multiple present', async ()=>{
|
||||||
@@ -618,13 +628,15 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
const req = { account: { username: 'test' } };
|
const req = { account: { username: 'test' } };
|
||||||
|
|
||||||
await api.deleteBrew(req, res);
|
await api.deleteBrew(req, res);
|
||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
|
expect(markModifiedFunc).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).not.toHaveBeenCalled();
|
expect(model.deleteOne).not.toHaveBeenCalled();
|
||||||
expect(saveFunc).toHaveBeenCalled();
|
expect(saveFunc).toHaveBeenCalled();
|
||||||
expect(saved.authors).toEqual(['test2']);
|
expect(saved.authors).toEqual(['test2']);
|
||||||
});
|
});
|
||||||
@@ -638,6 +650,7 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
saveFunc = jest.fn(async ()=>{ throw 'err'; });
|
saveFunc = jest.fn(async ()=>{ throw 'err'; });
|
||||||
const req = { account: { username: 'test' } };
|
const req = { account: { username: 'test' } };
|
||||||
|
|
||||||
@@ -651,7 +664,7 @@ brew`);
|
|||||||
expect(err).not.toBeUndefined();
|
expect(err).not.toBeUndefined();
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).not.toHaveBeenCalled();
|
expect(model.deleteOne).not.toHaveBeenCalled();
|
||||||
expect(saveFunc).toHaveBeenCalled();
|
expect(saveFunc).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -664,6 +677,7 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
api.deleteGoogleBrew = jest.fn(async ()=>true);
|
api.deleteGoogleBrew = jest.fn(async ()=>true);
|
||||||
const req = { account: { username: 'test' } };
|
const req = { account: { username: 'test' } };
|
||||||
|
|
||||||
@@ -671,7 +685,7 @@ brew`);
|
|||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).toHaveBeenCalled();
|
expect(model.deleteOne).toHaveBeenCalled();
|
||||||
expect(api.deleteGoogleBrew).toHaveBeenCalled();
|
expect(api.deleteGoogleBrew).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -684,6 +698,7 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
api.deleteGoogleBrew = jest.fn(async ()=>{
|
api.deleteGoogleBrew = jest.fn(async ()=>{
|
||||||
throw 'err';
|
throw 'err';
|
||||||
});
|
});
|
||||||
@@ -693,7 +708,7 @@ brew`);
|
|||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).toHaveBeenCalled();
|
expect(model.deleteOne).toHaveBeenCalled();
|
||||||
expect(api.deleteGoogleBrew).toHaveBeenCalled();
|
expect(api.deleteGoogleBrew).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -706,14 +721,16 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
api.deleteGoogleBrew = jest.fn(async ()=>true);
|
api.deleteGoogleBrew = jest.fn(async ()=>true);
|
||||||
const req = { account: { username: 'test' } };
|
const req = { account: { username: 'test' } };
|
||||||
|
|
||||||
await api.deleteBrew(req, res);
|
await api.deleteBrew(req, res);
|
||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
|
expect(markModifiedFunc).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).not.toHaveBeenCalled();
|
expect(model.deleteOne).not.toHaveBeenCalled();
|
||||||
expect(api.deleteGoogleBrew).toHaveBeenCalled();
|
expect(api.deleteGoogleBrew).toHaveBeenCalled();
|
||||||
expect(saveFunc).toHaveBeenCalled();
|
expect(saveFunc).toHaveBeenCalled();
|
||||||
expect(saved.authors).toEqual(['test2']);
|
expect(saved.authors).toEqual(['test2']);
|
||||||
@@ -731,6 +748,7 @@ brew`);
|
|||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
});
|
});
|
||||||
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
model.findOne = jest.fn(async ()=>modelBrew(brew));
|
||||||
|
model.deleteOne = jest.fn(async ()=>{});
|
||||||
api.deleteGoogleBrew = jest.fn(async ()=>true);
|
api.deleteGoogleBrew = jest.fn(async ()=>true);
|
||||||
const req = { account: { username: 'test2' } };
|
const req = { account: { username: 'test2' } };
|
||||||
|
|
||||||
@@ -738,7 +756,7 @@ brew`);
|
|||||||
|
|
||||||
expect(api.getBrew).toHaveBeenCalled();
|
expect(api.getBrew).toHaveBeenCalled();
|
||||||
expect(model.findOne).toHaveBeenCalled();
|
expect(model.findOne).toHaveBeenCalled();
|
||||||
expect(removeFunc).not.toHaveBeenCalled();
|
expect(model.deleteOne).not.toHaveBeenCalled();
|
||||||
expect(api.deleteGoogleBrew).not.toHaveBeenCalled();
|
expect(api.deleteGoogleBrew).not.toHaveBeenCalled();
|
||||||
expect(saveFunc).toHaveBeenCalled();
|
expect(saveFunc).toHaveBeenCalled();
|
||||||
expect(saved.authors).toEqual(['test']);
|
expect(saved.authors).toEqual(['test']);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const HomebrewSchema = mongoose.Schema({
|
|||||||
description : { type: String, default: '' },
|
description : { type: String, default: '' },
|
||||||
tags : [String],
|
tags : [String],
|
||||||
systems : [String],
|
systems : [String],
|
||||||
|
lang : { type: String, default: 'en' },
|
||||||
renderer : { type: String, default: '' },
|
renderer : { type: String, default: '' },
|
||||||
authors : [String],
|
authors : [String],
|
||||||
invitedAuthors : [String],
|
invitedAuthors : [String],
|
||||||
@@ -39,30 +40,24 @@ HomebrewSchema.statics.increaseView = async function(query) {
|
|||||||
return brew;
|
return brew;
|
||||||
};
|
};
|
||||||
|
|
||||||
HomebrewSchema.statics.get = function(query, fields=null){
|
HomebrewSchema.statics.get = async function(query, fields=null){
|
||||||
return new Promise((resolve, reject)=>{
|
const brew = await Homebrew.findOne(query, fields).orFail()
|
||||||
Homebrew.find(query, fields, null, (err, brews)=>{
|
.catch((error)=>{throw 'Can not find brew';});
|
||||||
if(err || !brews.length) return reject('Can not find brew');
|
if(!_.isNil(brew.textBin)) { // Uncompress zipped text field
|
||||||
if(!_.isNil(brews[0].textBin)) { // Uncompress zipped text field
|
unzipped = zlib.inflateRawSync(brew.textBin);
|
||||||
unzipped = zlib.inflateRawSync(brews[0].textBin);
|
brew.text = unzipped.toString();
|
||||||
brews[0].text = unzipped.toString();
|
}
|
||||||
}
|
return brew;
|
||||||
return resolve(brews[0]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HomebrewSchema.statics.getByUser = function(username, allowAccess=false, fields=null){
|
HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, fields=null){
|
||||||
return new Promise((resolve, reject)=>{
|
const query = { authors: username, published: true };
|
||||||
const query = { authors: username, published: true };
|
if(allowAccess){
|
||||||
if(allowAccess){
|
delete query.published;
|
||||||
delete query.published;
|
}
|
||||||
}
|
const brews = await Homebrew.find(query, fields).lean().exec() //lean() converts results to JSObjects
|
||||||
Homebrew.find(query, fields).lean().exec((err, brews)=>{ //lean() converts results to JSObjects
|
.catch((error)=>{throw 'Can not find brews';});
|
||||||
if(err) return reject('Can not find brew');
|
return brews;
|
||||||
return resolve(brews);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
|
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ const mustacheInjectInline = {
|
|||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
if(!lastToken)
|
if(!lastToken || lastToken.type == 'mustacheInjectInline')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const tags = ` ${processStyleTags(match[1])}`;
|
const tags = ` ${processStyleTags(match[1])}`;
|
||||||
@@ -169,7 +169,7 @@ const mustacheInjectBlock = {
|
|||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
if(!lastToken)
|
if(!lastToken || lastToken.type == 'mustacheInjectBlock')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
lastToken.originalType = 'mustacheInjectBlock';
|
lastToken.originalType = 'mustacheInjectBlock';
|
||||||
@@ -239,7 +239,7 @@ const definitionLists = {
|
|||||||
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists] });
|
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists] });
|
||||||
Marked.use(MarkedExtendedTables());
|
Marked.use(MarkedExtendedTables());
|
||||||
Marked.use(mustacheInjectBlock);
|
Marked.use(mustacheInjectBlock);
|
||||||
Marked.use({ smartypants: true });
|
Marked.use({ renderer: renderer, smartypants: true });
|
||||||
|
|
||||||
//Fix local links in the Preview iFrame to link inside the frame
|
//Fix local links in the Preview iFrame to link inside the frame
|
||||||
renderer.link = function (href, title, text) {
|
renderer.link = function (href, title, text) {
|
||||||
@@ -347,10 +347,7 @@ module.exports = {
|
|||||||
render : (rawBrewText)=>{
|
render : (rawBrewText)=>{
|
||||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
||||||
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
||||||
return Marked.parse(
|
return Marked.parse(sanatizeScriptTags(rawBrewText));
|
||||||
sanatizeScriptTags(rawBrewText),
|
|
||||||
{ renderer: renderer }
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
validate : (rawBrewText)=>{
|
validate : (rawBrewText)=>{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
vertical-align : middle;
|
vertical-align : middle;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
i{
|
i{
|
||||||
display : block;
|
display : block !important;
|
||||||
margin : 10px 0px;
|
margin : 10px 0px;
|
||||||
font-size : 6px;
|
font-size : 6px;
|
||||||
color : #666;
|
color : #666;
|
||||||
|
|||||||
@@ -13,3 +13,9 @@ test('Processes the markdown within an HTML block if its just a class wrapper',
|
|||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered).toBe('<div> <p><em>Bold text</em></p>\n </div>');
|
expect(rendered).toBe('<div> <p><em>Bold text</em></p>\n </div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Check markdown is using the custom renderer; specifically that it adds target=_self attribute to internal links in HTML blocks', function() {
|
||||||
|
const source = '<div>[Has _self Attribute?](#p1)</div>';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered).toBe('<div> <p><a href="#p1" target="_self">Has _self Attribute?</a></p>\n </div>');
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
/* eslint-disable max-lines */
|
|
||||||
|
|
||||||
const Markdown = require('naturalcrit/markdown.js');
|
|
||||||
|
|
||||||
test('Renders a mustache span with text only', function() {
|
|
||||||
const source = '{{ text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block ">text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text only, but with spaces', function() {
|
|
||||||
const source = '{{ this is a text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block ">this is a text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders an empty mustache span', function() {
|
|
||||||
const source = '{{}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block "></span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with just a space', function() {
|
|
||||||
const source = '{{ }}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block "></span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with a few spaces only', function() {
|
|
||||||
const source = '{{ }}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block "></span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and class', function() {
|
|
||||||
const source = '{{my-class text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
// FIXME: why do we have those two extra spaces after closing "?
|
|
||||||
expect(rendered).toBe('<span class="inline-block my-class" >text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and two classes', function() {
|
|
||||||
const source = '{{my-class,my-class2 text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
// FIXME: why do we have those two extra spaces after closing "?
|
|
||||||
expect(rendered).toBe('<span class="inline-block my-class my-class2" >text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text with spaces and class', function() {
|
|
||||||
const source = '{{my-class this is a text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
// FIXME: why do we have those two extra spaces after closing "?
|
|
||||||
expect(rendered).toBe('<span class="inline-block my-class" >this is a text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and id', function() {
|
|
||||||
const source = '{{#my-span text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
// FIXME: why do we have that one extra space after closing "?
|
|
||||||
expect(rendered).toBe('<span class="inline-block " id="my-span" >text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and two ids', function() {
|
|
||||||
const source = '{{#my-span,#my-favorite-span text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
// FIXME: do we need to report an error here somehow?
|
|
||||||
expect(rendered).toBe('<span class="inline-block " id="my-span" >text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and css property', function() {
|
|
||||||
const source = '{{color:red text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block " style="color:red;">text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and two css properties', function() {
|
|
||||||
const source = '{{color:red,padding:5px text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block " style="color:red; padding:5px;">text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and css property which contains quotes', function() {
|
|
||||||
const source = '{{font:"trebuchet ms" text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
// FIXME: is it correct to remove quotes surrounding css property value?
|
|
||||||
expect(rendered).toBe('<span class="inline-block " style="font:trebuchet ms;">text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text and two css properties which contains quotes', function() {
|
|
||||||
const source = '{{font:"trebuchet ms",padding:"5px 10px" text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block " style="font:trebuchet ms; padding:5px 10px;">text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('Renders a mustache span with text with quotes and css property which contains quotes', function() {
|
|
||||||
const source = '{{font:"trebuchet ms" text "with quotes"}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block " style="font:trebuchet ms;">text “with quotes”</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Renders a mustache span with text, id, class and a couple of css properties', function() {
|
|
||||||
const source = '{{pen,#author,color:orange,font-family:"trebuchet ms" text}}';
|
|
||||||
const rendered = Markdown.render(source);
|
|
||||||
expect(rendered).toBe('<span class="inline-block pen" id="author" style="color:orange; font-family:trebuchet ms;">text</span>');
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: add tests for ID with accordance to CSS spec:
|
|
||||||
//
|
|
||||||
// From https://drafts.csswg.org/selectors/#id-selectors:
|
|
||||||
//
|
|
||||||
// > An ID selector consists of a “number sign” (U+0023, #) immediately followed by the ID value, which must be a CSS identifier.
|
|
||||||
//
|
|
||||||
// From: https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier:
|
|
||||||
//
|
|
||||||
// > In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9]
|
|
||||||
// > and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_);
|
|
||||||
// > they cannot start with a digit, two hyphens, or a hyphen followed by a digit.
|
|
||||||
// > Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item).
|
|
||||||
// > For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".
|
|
||||||
// > Note that Unicode is code-by-code equivalent to ISO 10646 (see [UNICODE] and [ISO10646]).
|
|
||||||
|
|
||||||
// TODO: add tests for class with accordance to CSS spec:
|
|
||||||
//
|
|
||||||
// From: https://drafts.csswg.org/selectors/#class-html:
|
|
||||||
//
|
|
||||||
// > The class selector is given as a full stop (. U+002E) immediately followed by an identifier.
|
|
||||||
|
|
||||||
376
tests/markdown/mustache-syntax.test.js
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
|
// Marked.js adds line returns after closing tags on some default tokens.
|
||||||
|
// This removes those line returns for comparison sake.
|
||||||
|
String.prototype.trimReturns = function(){
|
||||||
|
return this.replace(/\r?\n|\r/g, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding `.failing()` method to `describe` or `it` will make failing tests "pass" as long as they continue to fail.
|
||||||
|
// Remove the `.failing()` method once you have fixed the issue.
|
||||||
|
|
||||||
|
describe('Inline: When using the Inline syntax {{ }}', ()=>{
|
||||||
|
it.failing('Renders a mustache span with text only', function() {
|
||||||
|
const source = '{{ text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text only, but with spaces', function() {
|
||||||
|
const source = '{{ this is a text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">this is a text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders an empty mustache span', function() {
|
||||||
|
const source = '{{}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with just a space', function() {
|
||||||
|
const source = '{{ }}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with a few spaces only', function() {
|
||||||
|
const source = '{{ }}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and class', function() {
|
||||||
|
const source = '{{my-class text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag.
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and two classes', function() {
|
||||||
|
const source = '{{my-class,my-class2 text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag.
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class my-class2">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text with spaces and class', function() {
|
||||||
|
const source = '{{my-class this is a text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class">this is a text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and id', function() {
|
||||||
|
const source = '{{#my-span text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s before closing `>` in opening tag, and another after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="my-span">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and two ids', function() {
|
||||||
|
const source = '{{#my-span,#my-favorite-span text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s before closing `>` in opening tag, and another after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="my-span">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and css property', function() {
|
||||||
|
const source = '{{color:red text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and two css properties', function() {
|
||||||
|
const source = '{{color:red,padding:5px text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red; padding:5px;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and css property which contains quotes', function() {
|
||||||
|
const source = '{{font-family:"trebuchet ms" text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text and two css properties which contains quotes', function() {
|
||||||
|
const source = '{{font-family:"trebuchet ms",padding:"5px 10px" text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms; padding:5px 10px;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it.failing('Renders a mustache span with text with quotes and css property which contains quotes', function() {
|
||||||
|
const source = '{{font-family:"trebuchet ms" text "with quotes"}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms;">text “with quotes”</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a mustache span with text, id, class and a couple of css properties', function() {
|
||||||
|
const source = '{{pen,#author,color:orange,font-family:"trebuchet ms" text}}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block pen" id="author" style="color:orange; font-family:trebuchet ms;">text</span>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// BLOCK SYNTAX
|
||||||
|
|
||||||
|
describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
||||||
|
it.failing('Renders a div with text only', function() {
|
||||||
|
const source = dedent`{{
|
||||||
|
text
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"><p>text</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders an empty div', function() {
|
||||||
|
const source = dedent`{{
|
||||||
|
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds extra \s after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a single paragraph with opening and closing brackets', function() {
|
||||||
|
const source = dedent`{{
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// this actually renders in HB as '{{ }}'...
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>{{}}</p>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a div with a single class', function() {
|
||||||
|
const source = dedent`{{cat
|
||||||
|
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a div with a single class and text', function() {
|
||||||
|
const source = dedent`{{cat
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a div with two classes and text', function() {
|
||||||
|
const source = dedent`{{cat,dog
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat dog"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a div with a style and text', function() {
|
||||||
|
const source = dedent`{{color:red
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds two extra \s before closing `>` in opening tag
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a div with a class, style and text', function() {
|
||||||
|
const source = dedent`{{cat,color:red
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds extra \s after the class attribute
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a div with an ID, class, style and text (different order)', function() {
|
||||||
|
const source = dedent`{{color:red,cat,#dog
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" id="dog" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a div with a single ID', function() {
|
||||||
|
const source = dedent`{{#cat,#dog
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// FIXME: adds extra \s before closing `>` in opening tag, and another after class names
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" id="cat"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// MUSTACHE INJECTION SYNTAX
|
||||||
|
|
||||||
|
describe('Injection: When an injection tag follows an element', ()=>{
|
||||||
|
// FIXME: Most of these fail because injections currently replace attributes, rather than append to. Or just minor extra whitespace issues.
|
||||||
|
describe('and that element is an inline-block', ()=>{
|
||||||
|
it.failing('Renders a span "text" with no injection', function() {
|
||||||
|
const source = '{{ text}}{}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a span "text" with injected Class name', function() {
|
||||||
|
const source = '{{ text}}{ClassName}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block ClassName">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a span "text" with injected style', function() {
|
||||||
|
const source = '{{ text}}{color:red}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a span "text" with two injected styles', function() {
|
||||||
|
const source = '{{ text}}{color:red,background:blue}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red; background:blue;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders an emphasis element with injected Class name', function() {
|
||||||
|
const source = '*emphasis*{big}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><em class="big">emphasis</em></p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders a code element with injected style', function() {
|
||||||
|
const source = '`code`{background:gray}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><code style="background:gray;">code</code></p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders an image element with injected style', function() {
|
||||||
|
const source = '{position:absolute}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><img src="http://i.imgur.com/hMna6G0.png" alt="homebrew mug" style="position:absolute;"></p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('Renders an element modified by only the first of two consecutive injections', function() {
|
||||||
|
const source = '{{ text}}{color:red}{background:blue}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><span class="inline-block" style="color:red;">text</span>{background:blue}</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and that element is a block', ()=>{
|
||||||
|
it.failing('renders a div "text" with no injection', function() {
|
||||||
|
const source = '{{\ntext\n}}\n{}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('renders a div "text" with injected Class name', function() {
|
||||||
|
const source = '{{\ntext\n}}\n{ClassName}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block ClassName"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('renders a div "text" with injected style', function() {
|
||||||
|
const source = '{{\ntext\n}}\n{color:red}';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" style="color:red;"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('renders a div "text" with two injected styles', function() {
|
||||||
|
const source = dedent`{{
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{color:red,background:blue}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" style="color:red; background:blue;"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('renders an h2 header "text" with injected class name', function() {
|
||||||
|
const source = dedent`## text
|
||||||
|
{ClassName}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName">text</h2>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('renders a table with injected class name', function() {
|
||||||
|
const source = dedent`| Experience Points | Level |
|
||||||
|
|:------------------|:-----:|
|
||||||
|
| 0 | 1 |
|
||||||
|
| 300 | 2 |
|
||||||
|
|
||||||
|
{ClassName}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<table class="ClassName"><thead><tr><th align=left>Experience Points</th><th align=center>Level</th></tr></thead><tbody><tr><td align=left>0</td><td align=center>1</td></tr><tr><td align=left>300</td><td align=center>2</td></tr></tbody></table>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// it('renders a list with with a style injected into the <ul> tag', function() {
|
||||||
|
// const source = dedent`- Cursed Ritual of Bad Hair
|
||||||
|
// - Eliminate Vindictiveness in Gym Teacher
|
||||||
|
// - Ultimate Rite of the Confetti Angel
|
||||||
|
// - Dark Chant of the Dentists
|
||||||
|
// - Divine Spell of Crossdressing
|
||||||
|
// {color:red}`;
|
||||||
|
// const rendered = Markdown.render(source).trimReturns();
|
||||||
|
// expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`...`); // FIXME: expect this to be injected into <ul>? Currently injects into last <li>
|
||||||
|
// });
|
||||||
|
|
||||||
|
it.failing('renders an h2 header "text" with injected class name, and "secondInjection" as regular text on the next line.', function() {
|
||||||
|
const source = dedent`## text
|
||||||
|
{ClassName}
|
||||||
|
{secondInjection}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName">text</h2><p>{secondInjection}</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.failing('renders a div nested into another div, the inner with class=innerDiv and the other class=outerDiv', function() {
|
||||||
|
const source = dedent`{{
|
||||||
|
outer text
|
||||||
|
{{
|
||||||
|
inner text
|
||||||
|
}}
|
||||||
|
{innerDiv}
|
||||||
|
}}
|
||||||
|
{outerDiv}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block outerDiv"><p>outer text</p><div class="block innerDiv"><p>inner text</p></div></div>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: add tests for ID with accordance to CSS spec:
|
||||||
|
//
|
||||||
|
// From https://drafts.csswg.org/selectors/#id-selectors:
|
||||||
|
//
|
||||||
|
// > An ID selector consists of a “number sign” (U+0023, #) immediately followed by the ID value, which must be a CSS identifier.
|
||||||
|
//
|
||||||
|
// From: https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier:
|
||||||
|
//
|
||||||
|
// > In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9]
|
||||||
|
// > and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_);
|
||||||
|
// > they cannot start with a digit, two hyphens, or a hyphen followed by a digit.
|
||||||
|
// > Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item).
|
||||||
|
// > For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".
|
||||||
|
// > Note that Unicode is code-by-code equivalent to ISO 10646 (see [UNICODE] and [ISO10646]).
|
||||||
|
|
||||||
|
// TODO: add tests for class with accordance to CSS spec:
|
||||||
|
//
|
||||||
|
// From: https://drafts.csswg.org/selectors/#class-html:
|
||||||
|
//
|
||||||
|
// > The class selector is given as a full stop (. U+002E) immediately followed by an identifier.
|
||||||
|
|
||||||
@@ -262,6 +262,7 @@ body {
|
|||||||
//Full Width
|
//Full Width
|
||||||
hr+hr+blockquote{
|
hr+hr+blockquote{
|
||||||
.useColumns(0.96);
|
.useColumns(0.96);
|
||||||
|
column-fill : balance;
|
||||||
}
|
}
|
||||||
//*****************************
|
//*****************************
|
||||||
// * FOOTER
|
// * FOOTER
|
||||||
|
|||||||
BIN
themes/V3/5eDMG/dropdownPreview.png
Normal file
|
After Width: | Height: | Size: 574 KiB |
BIN
themes/V3/5ePHB/dropdownPreview.png
Normal file
|
After Width: | Height: | Size: 782 KiB |
@@ -3,6 +3,7 @@
|
|||||||
const MagicGen = require('./snippets/magic.gen.js');
|
const MagicGen = require('./snippets/magic.gen.js');
|
||||||
const ClassTableGen = require('./snippets/classtable.gen.js');
|
const ClassTableGen = require('./snippets/classtable.gen.js');
|
||||||
const MonsterBlockGen = require('./snippets/monsterblock.gen.js');
|
const MonsterBlockGen = require('./snippets/monsterblock.gen.js');
|
||||||
|
const scriptGen = require('./snippets/script.gen.js');
|
||||||
const ClassFeatureGen = require('./snippets/classfeature.gen.js');
|
const ClassFeatureGen = require('./snippets/classfeature.gen.js');
|
||||||
const CoverPageGen = require('./snippets/coverpage.gen.js');
|
const CoverPageGen = require('./snippets/coverpage.gen.js');
|
||||||
const TableOfContentsGen = require('./snippets/tableOfContents.gen.js');
|
const TableOfContentsGen = require('./snippets/tableOfContents.gen.js');
|
||||||
@@ -169,9 +170,16 @@ module.exports = [
|
|||||||
gen : MonsterBlockGen.monster('monster,frame,wide', 4),
|
gen : MonsterBlockGen.monster('monster,frame,wide', 4),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Cover Page',
|
name : 'Cover Page',
|
||||||
icon : 'fas fa-file-word',
|
icon : 'fac book-front-cover',
|
||||||
gen : CoverPageGen,
|
gen : CoverPageGen.front,
|
||||||
|
experimental : true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Inside Cover Page',
|
||||||
|
icon : 'fac book-inside-cover',
|
||||||
|
gen : CoverPageGen.inside,
|
||||||
|
experimental : true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Magic Item',
|
name : 'Magic Item',
|
||||||
@@ -231,7 +239,30 @@ module.exports = [
|
|||||||
name : '1/3 Class Table (unframed)',
|
name : '1/3 Class Table (unframed)',
|
||||||
icon : 'fas fa-border-none',
|
icon : 'fas fa-border-none',
|
||||||
gen : ClassTableGen.third('classTable'),
|
gen : ClassTableGen.third('classTable'),
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
name : 'Rune Table',
|
||||||
|
icon : 'fas fa-language',
|
||||||
|
gen : scriptGen.dwarvish,
|
||||||
|
experimental : true,
|
||||||
|
subsnippets : [
|
||||||
|
{
|
||||||
|
name : 'Dwarvish',
|
||||||
|
icon : 'fac davek',
|
||||||
|
gen : scriptGen.dwarvish,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Elvish',
|
||||||
|
icon : 'fac rellanic',
|
||||||
|
gen : scriptGen.elvish,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Draconic',
|
||||||
|
icon : 'fac iokharic',
|
||||||
|
gen : scriptGen.draconic,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,46 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
const titles = [
|
const titles = [
|
||||||
'The Burning Gallows',
|
'The Burning Gallows', 'The Ring of Nenlast',
|
||||||
'The Ring of Nenlast',
|
'Below the Blind Tavern', 'Below the Hungering River',
|
||||||
'Below the Blind Tavern',
|
'Before Bahamut\'s Land', 'The Cruel Grave from Within',
|
||||||
'Below the Hungering River',
|
'The Strength of Trade Road', 'Through The Raven Queen\'s Worlds',
|
||||||
'Before Bahamut\'s Land',
|
'Within the Settlement', 'The Crown from Within',
|
||||||
'The Cruel Grave from Within',
|
'The Merchant Within the Battlefield', 'Ioun\'s Fading Traveler',
|
||||||
'The Strength of Trade Road',
|
'The Legion Ingredient', 'The Explorer Lure',
|
||||||
'Through The Raven Queen\'s Worlds',
|
'Before the Charming Badlands', 'Vecna\'s Hidden Sage',
|
||||||
'Within the Settlement',
|
'The Living Dead Above the Fearful Cage', 'Bahamut\'s Demonspawn',
|
||||||
'The Crown from Within',
|
'Across Gruumsh\'s Elemental Chaos', 'The Blade of Orcus',
|
||||||
'The Merchant Within the Battlefield',
|
'Beyond Revenge', 'Brain of Insanity',
|
||||||
'Ioun\'s Fading Traveler',
|
'A New Beginning', 'Evil Lake of the Merfolk',
|
||||||
'The Legion Ingredient',
|
'Invasion of the Gigantic Cat, Part II', 'Kraken War 2020',
|
||||||
'The Explorer Lure',
|
'The Body Whisperers', 'The Doctor from Heaven',
|
||||||
'Before the Charming Badlands',
|
'The Diabolical Tales of the Ape-Women', 'The Doctor Immortal',
|
||||||
'The Living Dead Above the Fearful Cage',
|
'Core of Heaven: Guardian of Amazement', 'The Graveyard',
|
||||||
'Vecna\'s Hidden Sage',
|
'Guardian: Skies of the Dark Wizard', 'Lute of Eternity',
|
||||||
'Bahamut\'s Demonspawn',
|
'Mercury\'s Planet: Brave Evolution', 'Azure Core',
|
||||||
'Across Gruumsh\'s Elemental Chaos',
|
'Sky of Zelda: The Thunder of Force', 'Core Battle',
|
||||||
'The Blade of Orcus',
|
'Ruby of Atlantis: The Quake of Peace', 'Deadly Amazement III',
|
||||||
'Beyond Revenge',
|
'Dry Chaos IX', 'Gate Thunder',
|
||||||
'Brain of Insanity',
|
'Vyse\'s Skies', 'Blue Greatness III',
|
||||||
'Breed Battle!, A New Beginning',
|
'Yellow Divinity', 'Zidane\'s Ghost'
|
||||||
'Evil Lake, A New Beginning',
|
|
||||||
'Invasion of the Gigantic Cat, Part II',
|
|
||||||
'Kraken War 2020',
|
|
||||||
'The Body Whisperers',
|
|
||||||
'The Diabolical Tales of the Ape-Women',
|
|
||||||
'The Doctor Immortal',
|
|
||||||
'The Doctor from Heaven',
|
|
||||||
'The Graveyard',
|
|
||||||
'Azure Core',
|
|
||||||
'Core Battle',
|
|
||||||
'Core of Heaven: The Guardian of Amazement',
|
|
||||||
'Deadly Amazement III',
|
|
||||||
'Dry Chaos IX',
|
|
||||||
'Gate Thunder',
|
|
||||||
'Guardian: Skies of the Dark Wizard',
|
|
||||||
'Lute of Eternity',
|
|
||||||
'Mercury\'s Planet: Brave Evolution',
|
|
||||||
'Ruby of Atlantis: The Quake of Peace',
|
|
||||||
'Sky of Zelda: The Thunder of Force',
|
|
||||||
'Vyse\'s Skies',
|
|
||||||
'White Greatness III',
|
|
||||||
'Yellow Divinity',
|
|
||||||
'Zidane\'s Ghost'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const subtitles = [
|
const subtitles = [
|
||||||
|
'Tomb of Shadows', 'Dragon\'s Lair',
|
||||||
|
'Lost Caverns', 'The Necromancer',
|
||||||
|
'Mystic Forest', 'Cursed Ruins',
|
||||||
|
'The Dark Abyss', 'Enchanted Maze',
|
||||||
|
'Haunted Castle', 'Sands of Fate',
|
||||||
|
'Dragon\'s Hoard', 'Undead Menace',
|
||||||
|
'Lost City Ruins', 'Goblin Ambush',
|
||||||
|
'Enchanted Forest', 'Darkness Rising',
|
||||||
|
'Quest for Glory', 'Ancient Prophecy',
|
||||||
|
'Shadowy Depths', 'Mystic Isles'
|
||||||
|
];
|
||||||
|
|
||||||
|
const footnote = [
|
||||||
'In an ominous universe, a botanist opposes terrorism.',
|
'In an ominous universe, a botanist opposes terrorism.',
|
||||||
'In a demon-haunted city, in an age of lies and hate, a physicist tries to find an ancient treasure and battles a mob of aliens.',
|
'In a demon-haunted city, in an age of lies and hate, a physicist tries to find an ancient treasure and battles a mob of aliens.',
|
||||||
'In a land of corruption, two cyberneticists and a dungeon delver search for freedom.',
|
'In a land of corruption, two cyberneticists and a dungeon delver search for freedom.',
|
||||||
@@ -74,51 +65,46 @@ const subtitles = [
|
|||||||
'On a planet of mysticism, three travelers and a fire fighter quest for the ultimate weapon and oppose evil.',
|
'On a planet of mysticism, three travelers and a fire fighter quest for the ultimate weapon and oppose evil.',
|
||||||
'In a wicked universe, five seers fight lawlessness.',
|
'In a wicked universe, five seers fight lawlessness.',
|
||||||
'In a kingdom of death, in an era of illusion and blood, four colonists search for fame.',
|
'In a kingdom of death, in an era of illusion and blood, four colonists search for fame.',
|
||||||
'In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.',
|
'In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.'
|
||||||
'In a cursed empire, five inventors oppose terrorism.',
|
|
||||||
'On a crime-ridden planet of conspiracy, a watchman and an artificial intelligence try to find love and oppose lawlessness.',
|
|
||||||
'In a forgotten land, a reporter and a spy try to stop the apocalypse.',
|
|
||||||
'In a forbidden land of prophecy, a scientist and an archivist oppose a cabal of barbarians intent on stealing the souls of the innocent.',
|
|
||||||
'On an infernal world of illusion, a grave robber and a watchman try to find revenge and combat a syndicate of mages intent on stealing the source of all magic.',
|
|
||||||
'In a galaxy of dark magic, four fighters seek freedom.',
|
|
||||||
'In an empire of deception, six tomb-robbers quest for the ultimate weapon and combat an army of raiders.',
|
|
||||||
'In a kingdom of corruption and lost souls, in an age of panic, eight planetologists oppose evil.',
|
|
||||||
'In a galaxy of misery and hopelessness, in a time of agony and pain, five planetologists search for vengance.',
|
|
||||||
'In a universe of technology and insanity, in a time of sorcery, a computer techician quests for hope.',
|
|
||||||
'On a planet of dark magic and barbarism, in an age of horror and blasphemy, seven librarians search for fame.',
|
|
||||||
'In an empire of dark magic, in a time of blood and illusions, four monks try to find the ultimate weapon and combat terrorism.',
|
|
||||||
'In a forgotten empire of dark magic, six kings try to prevent the destruction of mankind.',
|
|
||||||
'In a galaxy of dark magic and horror, in an age of hopelessness, four marines and an outlaw combat evil.',
|
|
||||||
'In a mysterious city of illusion, in an age of computerization, a witch-hunter tries to find the ultimate weapon and opposes an evil corporation.',
|
|
||||||
'In a damned kingdom of technology, a virtual reality programmer and a fighter seek fame.',
|
|
||||||
'In a hellish kingdom, in an age of blasphemy and blasphemy, an astrologer searches for fame.',
|
|
||||||
'In a damned world of devils, an alien and a ranger quest for love and oppose a syndicate of demons.',
|
|
||||||
'In a cursed galaxy, in a time of pain, seven librarians hope to avert the apocalypse.',
|
|
||||||
'In a crime-infested galaxy, in an era of hopelessness and panic, three champions and a grave robber try to solve the ultimate crime.'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
module.exports = ()=>{
|
front : function() {
|
||||||
return `<style>
|
return dedent`
|
||||||
.page#p1{ text-align:center; counter-increment: none; }
|
{{frontCover}}
|
||||||
.page#p1:after{ display:none; }
|
|
||||||
.page:nth-child(2n) .pageNumber { left: inherit !important; right: 2px !important; }
|
|
||||||
.page:nth-child(2n+1) .pageNumber { right: inherit !important; left: 2px !important; }
|
|
||||||
.page:nth-child(2n)::after { transform: scaleX(1); }
|
|
||||||
.page:nth-child(2n+1)::after { transform: scaleX(-1); }
|
|
||||||
.page:nth-child(2n) .footnote { left: inherit; text-align: right; }
|
|
||||||
.page:nth-child(2n+1) .footnote { left: 80px; text-align: left; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
{{margin-top:225px}}
|
{{logo }}
|
||||||
|
|
||||||
# ${_.sample(titles)}
|
# ${_.sample(titles)}
|
||||||
|
## ${_.sample(subtitles)}
|
||||||
|
___
|
||||||
|
|
||||||
{{margin-top:25px}}
|
{{banner HOMEBREW}}
|
||||||
|
|
||||||
{{wide
|
{{footnote
|
||||||
##### ${_.sample(subtitles)}
|
${_.sample(footnote)}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
\\page`;
|
{position:absolute,bottom:0,left:0,height:100%}
|
||||||
};
|
|
||||||
|
\page`;
|
||||||
|
},
|
||||||
|
|
||||||
|
inside : function() {
|
||||||
|
return dedent`
|
||||||
|
{{insideCover}}
|
||||||
|
|
||||||
|
# ${_.sample(titles)}
|
||||||
|
## ${_.sample(subtitles)}
|
||||||
|
___
|
||||||
|
|
||||||
|
{{imageMaskCenter${_.random(1, 16)},--offsetX:0%,--offsetY:0%,--rotation:0
|
||||||
|
{height:100%}
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{logo }}
|
||||||
|
|
||||||
|
\page`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
48
themes/V3/5ePHB/snippets/script.gen.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
dwarvish : ()=>{
|
||||||
|
return dedent `##### Dwarvish Runes: Sample Alphabet
|
||||||
|
{{runeTable,wide,frame,font-family:Davek
|
||||||
|
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||||
|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||||
|
:
|
||||||
|
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||||
|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||||
|
}}\n\n`;
|
||||||
|
},
|
||||||
|
elvish : ()=>{
|
||||||
|
return dedent `##### Elvish Runes: Sample Alphabet
|
||||||
|
{{runeTable,wide,frame,font-family:Rellanic
|
||||||
|
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||||
|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||||
|
:
|
||||||
|
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||||
|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||||
|
}}\n\n`;
|
||||||
|
},
|
||||||
|
draconic : ()=>{
|
||||||
|
return dedent `##### Draconic Runes: Sample Alphabet
|
||||||
|
{{runeTable,wide,frame,font-family:Iokharic
|
||||||
|
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||||
|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||||
|
:
|
||||||
|
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||||
|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||||
|
}}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
()=>{
|
||||||
|
|
||||||
|
};
|
||||||
@@ -253,7 +253,7 @@ body {
|
|||||||
border-image-outset : 9px 0px;
|
border-image-outset : 9px 0px;
|
||||||
border-image-width : 11px;
|
border-image-width : 11px;
|
||||||
padding : 0.13cm 0.16cm;
|
padding : 0.13cm 0.16cm;
|
||||||
filter : drop-shadow(1px 4px 6px #888);
|
box-shadow : 1px 4px 14px #888;
|
||||||
.page :where(&) {
|
.page :where(&) {
|
||||||
margin-top : 9px; //Prevent top border getting cut off on colbreak
|
margin-top : 9px; //Prevent top border getting cut off on colbreak
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ body {
|
|||||||
border-image : @descriptiveBoxImage 12 stretch;
|
border-image : @descriptiveBoxImage 12 stretch;
|
||||||
border-image-outset : 4px;
|
border-image-outset : 4px;
|
||||||
padding : 0.1em;
|
padding : 0.1em;
|
||||||
filter : drop-shadow(0 0 3px #faf7ea);
|
box-shadow : 0 0 6px #faf7ea;
|
||||||
.page :where(&) {
|
.page :where(&) {
|
||||||
margin-top : 4px; //Prevent top border getting cut off on colbreak
|
margin-top : 4px; //Prevent top border getting cut off on colbreak
|
||||||
}
|
}
|
||||||
@@ -397,7 +397,7 @@ body {
|
|||||||
border-image-outset : 0px 2px;
|
border-image-outset : 0px 2px;
|
||||||
background-blend-mode : overlay;
|
background-blend-mode : overlay;
|
||||||
background-attachment : fixed;
|
background-attachment : fixed;
|
||||||
filter : drop-shadow(1px 4px 6px #888);
|
box-shadow : 1px 4px 14px #888;
|
||||||
padding : 4px 2px;
|
padding : 4px 2px;
|
||||||
margin-left : -0.16cm;
|
margin-left : -0.16cm;
|
||||||
margin-right : -0.16cm;
|
margin-right : -0.16cm;
|
||||||
@@ -624,134 +624,278 @@ body {
|
|||||||
// * CLASS TABLE
|
// * CLASS TABLE
|
||||||
// *****************************/
|
// *****************************/
|
||||||
.page .classTable{
|
.page .classTable{
|
||||||
th[colspan]:not([rowspan]) {
|
th[colspan]:not([rowspan]) {
|
||||||
white-space : nowrap;
|
white-space : nowrap;
|
||||||
|
}
|
||||||
|
&.frame {
|
||||||
|
margin-top : 0.7cm;
|
||||||
|
margin-bottom : 0.9cm;
|
||||||
|
margin-left : -0.1cm;
|
||||||
|
margin-right : -0.1cm;
|
||||||
|
width : calc(100% + 0.2cm);
|
||||||
|
border-collapse : separate;
|
||||||
|
background-color : white;
|
||||||
|
border : initial;
|
||||||
|
border-style : solid;
|
||||||
|
border-image-outset : 0.4cm 0.3cm;
|
||||||
|
border-image-repeat : stretch;
|
||||||
|
border-image-slice : 200;
|
||||||
|
border-image-source : @frameBorderImage;
|
||||||
|
border-image-width : 47px;
|
||||||
|
&.wide:first-child {
|
||||||
|
margin-top: 0.12cm;
|
||||||
}
|
}
|
||||||
&.frame {
|
& + * {
|
||||||
margin-top : 0.7cm;
|
margin-top: 0;
|
||||||
margin-bottom : 0.9cm;
|
|
||||||
margin-left : -0.1cm;
|
|
||||||
margin-right : -0.1cm;
|
|
||||||
width : calc(100% + 0.2cm);
|
|
||||||
border-collapse : separate;
|
|
||||||
background-color : white;
|
|
||||||
border : initial;
|
|
||||||
border-style : solid;
|
|
||||||
border-image-outset : 0.4cm 0.3cm;
|
|
||||||
border-image-repeat : stretch;
|
|
||||||
border-image-slice : 200;
|
|
||||||
border-image-source : @frameBorderImage;
|
|
||||||
border-image-width : 47px;
|
|
||||||
&.wide:first-child {
|
|
||||||
margin-top: 0.12cm;
|
|
||||||
}
|
|
||||||
& + * {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.decoration {
|
|
||||||
position:relative;
|
|
||||||
}
|
|
||||||
&.decoration::before {
|
|
||||||
content :'';
|
|
||||||
position : absolute;
|
|
||||||
background-image : @classTableDecoration,
|
|
||||||
@classTableDecoration;
|
|
||||||
background-size : contain, contain;
|
|
||||||
background-repeat : no-repeat, no-repeat;
|
|
||||||
background-position : top, bottom;
|
|
||||||
width : 7.75cm;
|
|
||||||
height : calc(100% + 3.3cm);
|
|
||||||
top : 50%;
|
|
||||||
left : 50%;
|
|
||||||
transform : translateY(-50%) translateX(-50%);
|
|
||||||
filter : drop-shadow(0px 0px 1px #C8C5C080);
|
|
||||||
z-index : -1;
|
|
||||||
}
|
|
||||||
&.decoration.wide::before {
|
|
||||||
width : calc(100% + 3.3cm);
|
|
||||||
height : 7.75cm;
|
|
||||||
background-position : left, right;
|
|
||||||
}
|
|
||||||
h5 + table{
|
|
||||||
margin-top : 0.2cm;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.decoration {
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
&.decoration::before {
|
||||||
|
content :'';
|
||||||
|
position : absolute;
|
||||||
|
background-image : @classTableDecoration,
|
||||||
|
@classTableDecoration;
|
||||||
|
background-size : contain, contain;
|
||||||
|
background-repeat : no-repeat, no-repeat;
|
||||||
|
background-position : top, bottom;
|
||||||
|
width : 7.75cm;
|
||||||
|
height : calc(100% + 3.3cm);
|
||||||
|
top : 50%;
|
||||||
|
left : 50%;
|
||||||
|
transform : translateY(-50%) translateX(-50%);
|
||||||
|
filter : drop-shadow(0px 0px 1px #C8C5C080);
|
||||||
|
z-index : -1;
|
||||||
|
}
|
||||||
|
&.decoration.wide::before {
|
||||||
|
width : calc(100% + 3.3cm);
|
||||||
|
height : 7.75cm;
|
||||||
|
background-position : left, right;
|
||||||
|
}
|
||||||
|
h5 + table{
|
||||||
|
margin-top : 0.2cm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*****************************
|
||||||
|
// * FRONT COVER PAGE
|
||||||
|
// *****************************/
|
||||||
|
.page:has(.frontCover) {
|
||||||
|
columns : 1;
|
||||||
|
text-align : center;
|
||||||
|
&:after {
|
||||||
|
all: unset;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-shadow: unset;
|
||||||
|
filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
||||||
|
text-transform : uppercase;
|
||||||
|
font-weight : normal;
|
||||||
|
margin-top : 1.2cm;
|
||||||
|
margin-bottom : 0;
|
||||||
|
color : white;
|
||||||
|
font-family : NodestoCapsCondensed;
|
||||||
|
font-size : 2.245cm;
|
||||||
|
line-height : 0.85em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
||||||
|
font-family : NodestoCapsCondensed;
|
||||||
|
font-weight : normal;
|
||||||
|
font-size : 0.85cm;
|
||||||
|
letter-spacing : 0.1cm;
|
||||||
|
color : white;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
display : block;
|
||||||
|
position : relative;
|
||||||
|
background-image : @horizontalRule;
|
||||||
|
background-size : 100% 100%;
|
||||||
|
visibility : visible;
|
||||||
|
height : 0.5cm;
|
||||||
|
width : 12cm;
|
||||||
|
border : none;
|
||||||
|
margin : auto;
|
||||||
|
filter : drop-shadow(0 0 3px black);
|
||||||
|
}
|
||||||
|
.banner {
|
||||||
|
filter : drop-shadow(2px 2px 2px black);
|
||||||
|
position : absolute;
|
||||||
|
left : 0;
|
||||||
|
bottom : 4.2cm;
|
||||||
|
background-image : url('/assets/coverPageBanner.svg');
|
||||||
|
height : 1.7cm;
|
||||||
|
width : 10.5cm;
|
||||||
|
color : white;
|
||||||
|
font-family : NodestoCapsCondensed;
|
||||||
|
font-weight : normal;
|
||||||
|
font-size : 1cm;
|
||||||
|
letter-spacing : 0.014cm;
|
||||||
|
text-align : left;
|
||||||
|
padding-left : 1cm;
|
||||||
|
display : flex;
|
||||||
|
justify-content : center;
|
||||||
|
flex-direction : column;
|
||||||
|
padding-top : 0.1cm;
|
||||||
|
}
|
||||||
|
.footnote {
|
||||||
|
filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||||
|
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
||||||
|
position : absolute;
|
||||||
|
text-align : center;
|
||||||
|
color : white;
|
||||||
|
font-size : 0.496cm;
|
||||||
|
bottom : 1.3cm;
|
||||||
|
left : 0;
|
||||||
|
right : 0;
|
||||||
|
margin-left : auto;
|
||||||
|
margin-right : auto;
|
||||||
|
width : 70%;
|
||||||
|
font-family : Overpass;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
position : absolute;
|
||||||
|
top : 0.5cm;
|
||||||
|
left : 0;
|
||||||
|
right : 0;
|
||||||
|
filter : drop-shadow(0 0 0.075cm black);
|
||||||
|
img {
|
||||||
|
height : 2cm;
|
||||||
|
width : 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*****************************
|
||||||
|
// * INSIDE COVER PAGE
|
||||||
|
// *****************************/
|
||||||
|
.page:has(.insideCover) {
|
||||||
|
columns : 1;
|
||||||
|
text-align : center;
|
||||||
|
&:after {
|
||||||
|
all : unset;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-family : NodestoCapsCondensed;
|
||||||
|
font-weight : normal;
|
||||||
|
font-size : 2.1cm;
|
||||||
|
margin-top : 1.2cm;
|
||||||
|
margin-bottom : 0;
|
||||||
|
text-transform : uppercase;
|
||||||
|
line-height : 0.85em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-family : NodestoCapsCondensed;
|
||||||
|
font-weight : normal;
|
||||||
|
font-size : 0.85cm;
|
||||||
|
letter-spacing : 0.5cm;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
display : block;
|
||||||
|
position : relative;
|
||||||
|
background-image : @horizontalRule;
|
||||||
|
background-size : 100% 100%;
|
||||||
|
visibility : visible;
|
||||||
|
height : 0.5cm;
|
||||||
|
width : 12cm;
|
||||||
|
border : none;
|
||||||
|
margin : auto;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
position : absolute;
|
||||||
|
bottom : 1cm;
|
||||||
|
left : 0;
|
||||||
|
right : 0;
|
||||||
|
height : 2cm;
|
||||||
|
img {
|
||||||
|
height : 2cm;
|
||||||
|
width : 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//*****************************
|
//*****************************
|
||||||
// * TABLE OF CONTENTS
|
// * TABLE OF CONTENTS
|
||||||
// *****************************/
|
// *****************************/
|
||||||
.page {
|
.page {
|
||||||
&:has(.toc):after {
|
&:has(.toc):after {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
.toc {
|
||||||
|
-webkit-column-break-inside : avoid;
|
||||||
|
page-break-inside : avoid;
|
||||||
|
break-inside : avoid;
|
||||||
|
h1 {
|
||||||
|
text-align : center;
|
||||||
|
margin-bottom : 0.3cm;
|
||||||
}
|
}
|
||||||
.toc {
|
a{
|
||||||
-webkit-column-break-inside : avoid;
|
display : inline;
|
||||||
page-break-inside : avoid;
|
color : inherit;
|
||||||
break-inside : avoid;
|
text-decoration : none;
|
||||||
h1 {
|
&:hover{
|
||||||
text-align : center;
|
text-decoration : underline;
|
||||||
margin-bottom : 0.3cm;
|
|
||||||
}
|
|
||||||
a{
|
|
||||||
display : inline;
|
|
||||||
color : inherit;
|
|
||||||
text-decoration : none;
|
|
||||||
&:hover{
|
|
||||||
text-decoration : underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
margin-top : 0.2cm;
|
|
||||||
line-height : 0.4cm;
|
|
||||||
& + ul li {
|
|
||||||
line-height: 1.2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ul{
|
|
||||||
padding-left : 0;
|
|
||||||
list-style-type : none;
|
|
||||||
margin-top : 0;
|
|
||||||
a {
|
|
||||||
width : 100%;
|
|
||||||
display : flex;
|
|
||||||
flex-flow : row nowrap;
|
|
||||||
justify-content : space-between;
|
|
||||||
}
|
|
||||||
li + li h3 {
|
|
||||||
margin-top : 0.26cm;
|
|
||||||
line-height : 1em
|
|
||||||
}
|
|
||||||
h3 span:first-child::after {
|
|
||||||
border : none;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
display : contents;
|
|
||||||
&:first-child::after {
|
|
||||||
content : "";
|
|
||||||
bottom : 0.08cm;
|
|
||||||
flex : 1;
|
|
||||||
margin-left : 0.08cm; /* Spacing before dot leaders */
|
|
||||||
margin-right : 0.16cm;
|
|
||||||
border-bottom : 0.05cm dotted #000;
|
|
||||||
margin-bottom : 0.08cm;
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
display : inline-block;
|
|
||||||
align-self : flex-end;
|
|
||||||
font-family : "BookInsanityRemake";
|
|
||||||
font-size : 0.34cm;
|
|
||||||
font-weight : normal;
|
|
||||||
color : #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ul { /*List indent*/
|
|
||||||
margin-left : 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.wide{
|
|
||||||
.useColumns(0.96, @fillMode: balance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h4 {
|
||||||
|
margin-top : 0.2cm;
|
||||||
|
line-height : 0.4cm;
|
||||||
|
& + ul li {
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul{
|
||||||
|
padding-left : 0;
|
||||||
|
list-style-type : none;
|
||||||
|
margin-top : 0;
|
||||||
|
a {
|
||||||
|
width : 100%;
|
||||||
|
display : flex;
|
||||||
|
flex-flow : row nowrap;
|
||||||
|
justify-content : space-between;
|
||||||
|
}
|
||||||
|
li + li h3 {
|
||||||
|
margin-top : 0.26cm;
|
||||||
|
line-height : 1em
|
||||||
|
}
|
||||||
|
h3 span:first-child::after {
|
||||||
|
border : none;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display : contents;
|
||||||
|
&:first-child::after {
|
||||||
|
content : "";
|
||||||
|
bottom : 0.08cm;
|
||||||
|
flex : 1;
|
||||||
|
margin-left : 0.08cm; /* Spacing before dot leaders */
|
||||||
|
margin-right : 0.16cm;
|
||||||
|
border-bottom : 0.05cm dotted #000;
|
||||||
|
margin-bottom : 0.08cm;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
display : inline-block;
|
||||||
|
align-self : flex-end;
|
||||||
|
font-family : "BookInsanityRemake";
|
||||||
|
font-size : 0.34cm;
|
||||||
|
font-weight : normal;
|
||||||
|
color : #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul { /*List indent*/
|
||||||
|
margin-left : 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.wide{
|
||||||
|
.useColumns(0.96, @fillMode: balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//*****************************
|
//*****************************
|
||||||
@@ -794,3 +938,43 @@ body {
|
|||||||
.page h1 + *{
|
.page h1 + *{
|
||||||
margin-top : 0;
|
margin-top : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//*****************************
|
||||||
|
// * RUNE TABLE
|
||||||
|
// *****************************/
|
||||||
|
.page {
|
||||||
|
.runeTable {
|
||||||
|
margin-block: 0.7cm;
|
||||||
|
table {
|
||||||
|
font-family : inherit;
|
||||||
|
tbody tr {
|
||||||
|
background: unset;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
width: 1.3cm;
|
||||||
|
height: 1.3cm;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-transform: uppercase;
|
||||||
|
outline: 1px solid #000;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
th{
|
||||||
|
font-family: BookInsanityRemake;
|
||||||
|
font-size: 0.45cm;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
font-size: 0.7cm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.frame {
|
||||||
|
border: initial;
|
||||||
|
border-style: solid;
|
||||||
|
border-image-outset: .45cm .35cm .4cm .4cm;
|
||||||
|
border-image-repeat: stretch;
|
||||||
|
border-image-slice: 170;
|
||||||
|
border-image-source: @scriptBorder;
|
||||||
|
border-image-width: 1.4cm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
BIN
themes/V3/Blank/dropdownPreview.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
@@ -1,9 +1,8 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
const WatercolorGen = require('./snippets/watercolor.gen.js');
|
const WatercolorGen = require('./snippets/watercolor.gen.js');
|
||||||
const dedent = require('dedent-tabs').default;
|
const ImageMaskGen = require('./snippets/imageMask.gen.js');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
|
|
||||||
@@ -102,6 +101,68 @@ module.exports = [
|
|||||||
icon : 'fas fa-fill-drip',
|
icon : 'fas fa-fill-drip',
|
||||||
gen : WatercolorGen,
|
gen : WatercolorGen,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name : 'Watercolor Center',
|
||||||
|
icon : 'fac mask-center',
|
||||||
|
gen : ImageMaskGen.center,
|
||||||
|
experimental : true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Watercolor Edge',
|
||||||
|
icon : 'fac mask-edge',
|
||||||
|
gen : ImageMaskGen.edge('bottom'),
|
||||||
|
experimental : true,
|
||||||
|
subsnippets : [
|
||||||
|
{
|
||||||
|
name : 'Top',
|
||||||
|
icon : 'fac position-top',
|
||||||
|
gen : ImageMaskGen.edge('top'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Right',
|
||||||
|
icon : 'fac position-right',
|
||||||
|
gen : ImageMaskGen.edge('right'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Bottom',
|
||||||
|
icon : 'fac position-bottom',
|
||||||
|
gen : ImageMaskGen.edge('bottom'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Left',
|
||||||
|
icon : 'fac position-left',
|
||||||
|
gen : ImageMaskGen.edge('left'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Watercolor Corner',
|
||||||
|
icon : 'fac mask-corner',
|
||||||
|
gen : ImageMaskGen.corner,
|
||||||
|
experimental : true,
|
||||||
|
subsnippets : [
|
||||||
|
{
|
||||||
|
name : 'Top-Left',
|
||||||
|
icon : 'fac position-top-left',
|
||||||
|
gen : ImageMaskGen.corner('top', 'left'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Top-Right',
|
||||||
|
icon : 'fac position-top-right',
|
||||||
|
gen : ImageMaskGen.corner('top', 'right'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Bottom-Left',
|
||||||
|
icon : 'fac position-bottom-left',
|
||||||
|
gen : ImageMaskGen.corner('bottom', 'left'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Bottom-Right',
|
||||||
|
icon : 'fac position-bottom-right',
|
||||||
|
gen : ImageMaskGen.corner('bottom', 'right'),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name : 'Watermark',
|
name : 'Watermark',
|
||||||
icon : 'fas fa-id-card',
|
icon : 'fas fa-id-card',
|
||||||
|
|||||||
46
themes/V3/Blank/snippets/imageMask.gen.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
center : ()=>{
|
||||||
|
return dedent`
|
||||||
|
{{imageMaskCenter${_.random(1, 16)},--offsetX:0%,--offsetY:0%,--rotation:0
|
||||||
|
{height:100%}
|
||||||
|
}}
|
||||||
|
<!-- Use --offsetX to shift the mask left or right (can use cm instead of %)
|
||||||
|
Use --offsetY to shift the mask up or down
|
||||||
|
Use --rotation to set rotation angle in degrees. -->\n\n`;
|
||||||
|
},
|
||||||
|
|
||||||
|
edge : (side = 'bottom')=>{
|
||||||
|
const rotation = {
|
||||||
|
'bottom' : 0,
|
||||||
|
'top' : 180,
|
||||||
|
'left' : 90,
|
||||||
|
'right' : 270
|
||||||
|
}[side];
|
||||||
|
return dedent`
|
||||||
|
{{imageMaskEdge${_.random(1, 8)},--offset:0%,--rotation:${rotation}
|
||||||
|
{height:100%}
|
||||||
|
}}
|
||||||
|
<!-- Use --offset to shift the mask away from page center (can use cm instead of %)
|
||||||
|
Use --rotation to set rotation angle in degrees. -->\n\n`;
|
||||||
|
},
|
||||||
|
|
||||||
|
corner : (y = 'top', x = 'left')=>{
|
||||||
|
const offsetX = (x == 'left' ? '-50%' : '50%');
|
||||||
|
const offsetY = (y == 'top' ? '50%' : '-50%');
|
||||||
|
return dedent`
|
||||||
|
{{imageMaskCorner${_.random(1, 37)},--offsetX:${offsetX},--offsetY:${offsetY},--rotation:0
|
||||||
|
{height:100%}
|
||||||
|
}}
|
||||||
|
<!-- Use --offsetX to shift the mask left or right (can use cm instead of %)
|
||||||
|
Use --offsetY to shift the mask up or down
|
||||||
|
Use --rotation to set rotation angle in degrees. -->\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
()=>{
|
||||||
|
|
||||||
|
};
|
||||||
@@ -15,6 +15,21 @@ body {
|
|||||||
-webkit-print-color-adjust : exact;
|
-webkit-print-color-adjust : exact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//*****************************
|
||||||
|
// * MUSTACHE DIVS/SPANS
|
||||||
|
// *****************************/
|
||||||
|
.page {
|
||||||
|
.block {
|
||||||
|
break-inside : avoid;
|
||||||
|
display : inline-block;
|
||||||
|
width : 100%;
|
||||||
|
}
|
||||||
|
.inline-block {
|
||||||
|
display : inline-block;
|
||||||
|
text-indent : initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.useColumns(@multiplier : 1, @fillMode: balance){
|
.useColumns(@multiplier : 1, @fillMode: balance){
|
||||||
column-fill : @fillMode;
|
column-fill : @fillMode;
|
||||||
column-count : 2;
|
column-count : 2;
|
||||||
@@ -115,59 +130,6 @@ body {
|
|||||||
margin-top : 0; // have vertical spacing.
|
margin-top : 0; // have vertical spacing.
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Watermark */
|
|
||||||
.watermark {
|
|
||||||
display : grid !important;
|
|
||||||
place-items : center;
|
|
||||||
justify-content : center;
|
|
||||||
position : absolute;
|
|
||||||
margin : 0;
|
|
||||||
top : 0;
|
|
||||||
left : 0;
|
|
||||||
width : 100%;
|
|
||||||
height : 100%;
|
|
||||||
font-size : 120px;
|
|
||||||
text-transform : uppercase;
|
|
||||||
color : black;
|
|
||||||
mix-blend-mode : overlay;
|
|
||||||
opacity : 30%;
|
|
||||||
transform : rotate(-45deg);
|
|
||||||
z-index : 500;
|
|
||||||
p {
|
|
||||||
margin-bottom : none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Watercolor */
|
|
||||||
[class*="watercolor"] {
|
|
||||||
position : absolute;
|
|
||||||
width : 2000px; /* dimensions need to be real big so the user can set */
|
|
||||||
height : 2000px; /* height or width and the image will maintain aspect ratio */
|
|
||||||
-webkit-mask-image : var(--wc);
|
|
||||||
-webkit-mask-size : contain;
|
|
||||||
-webkit-mask-repeat : no-repeat;
|
|
||||||
mask-image : var(--wc);
|
|
||||||
mask-size : contain;
|
|
||||||
mask-repeat : no-repeat;
|
|
||||||
background-size : cover;
|
|
||||||
background-color : var(--HB_Color_WatercolorStain); /*default color*/
|
|
||||||
--wc : @watercolor1; /*default image*/
|
|
||||||
z-index : -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.watercolor1 { --wc : @watercolor1; }
|
|
||||||
.watercolor2 { --wc : @watercolor2; }
|
|
||||||
.watercolor3 { --wc : @watercolor3; }
|
|
||||||
.watercolor4 { --wc : @watercolor4; }
|
|
||||||
.watercolor5 { --wc : @watercolor5; }
|
|
||||||
.watercolor6 { --wc : @watercolor6; }
|
|
||||||
.watercolor7 { --wc : @watercolor7; }
|
|
||||||
.watercolor8 { --wc : @watercolor8; }
|
|
||||||
.watercolor9 { --wc : @watercolor9; }
|
|
||||||
.watercolor10 { --wc : @watercolor10; }
|
|
||||||
.watercolor11 { --wc : @watercolor11; }
|
|
||||||
.watercolor12 { --wc : @watercolor12; }
|
|
||||||
|
|
||||||
//************************************
|
//************************************
|
||||||
// * CODE BLOCKS
|
// * CODE BLOCKS
|
||||||
// ************************************/
|
// ************************************/
|
||||||
@@ -211,20 +173,245 @@ body {
|
|||||||
page-break-inside : avoid;
|
page-break-inside : avoid;
|
||||||
break-inside : avoid;
|
break-inside : avoid;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//*****************************
|
/* Watermark */
|
||||||
// * MUSTACHE DIVS/SPANS
|
.watermark {
|
||||||
// *****************************/
|
display : grid !important;
|
||||||
.page {
|
place-items : center;
|
||||||
.block {
|
justify-content : center;
|
||||||
break-inside : avoid;
|
position : absolute;
|
||||||
display : inline-block;
|
margin : 0;
|
||||||
width : 100%;
|
top : 0;
|
||||||
|
left : 0;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
font-size : 120px;
|
||||||
|
text-transform : uppercase;
|
||||||
|
mix-blend-mode : overlay;
|
||||||
|
opacity : 30%;
|
||||||
|
transform : rotate(-45deg);
|
||||||
|
z-index : 500;
|
||||||
|
p {
|
||||||
|
margin-bottom : none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.inline-block {
|
|
||||||
display : inline-block;
|
/* Watercolor */
|
||||||
text-indent : initial;
|
[class*="watercolor"] {
|
||||||
|
position : absolute;
|
||||||
|
width : 2000px; /* dimensions need to be real big so the user can set */
|
||||||
|
height : 2000px; /* height or width and the image will maintain aspect ratio */
|
||||||
|
-webkit-mask-image : var(--wc);
|
||||||
|
-webkit-mask-size : contain;
|
||||||
|
-webkit-mask-repeat : no-repeat;
|
||||||
|
mask-image : var(--wc);
|
||||||
|
mask-size : contain;
|
||||||
|
mask-repeat : no-repeat;
|
||||||
|
background-size : cover;
|
||||||
|
background-color : var(--HB_Color_WatercolorStain); /*default color*/
|
||||||
|
--wc : @watercolor1; /*default image*/
|
||||||
|
z-index : -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.watercolor1 { --wc : @watercolor1; }
|
||||||
|
.watercolor2 { --wc : @watercolor2; }
|
||||||
|
.watercolor3 { --wc : @watercolor3; }
|
||||||
|
.watercolor4 { --wc : @watercolor4; }
|
||||||
|
.watercolor5 { --wc : @watercolor5; }
|
||||||
|
.watercolor6 { --wc : @watercolor6; }
|
||||||
|
.watercolor7 { --wc : @watercolor7; }
|
||||||
|
.watercolor8 { --wc : @watercolor8; }
|
||||||
|
.watercolor9 { --wc : @watercolor9; }
|
||||||
|
.watercolor10 { --wc : @watercolor10; }
|
||||||
|
.watercolor11 { --wc : @watercolor11; }
|
||||||
|
.watercolor12 { --wc : @watercolor12; }
|
||||||
|
|
||||||
|
/* Image Masks */
|
||||||
|
|
||||||
|
[class*="imageMask"] {
|
||||||
|
position : absolute;
|
||||||
|
height : 200%;
|
||||||
|
width : 200%;
|
||||||
|
left : 50%;
|
||||||
|
bottom : 50%;
|
||||||
|
--rotation : 0;
|
||||||
|
--revealer : none;
|
||||||
|
--checkerboard : none;
|
||||||
|
--scaleX : 1;
|
||||||
|
--scaleY : 1;
|
||||||
|
-webkit-mask-image : var(--wc), var(--revealer);
|
||||||
|
-webkit-mask-repeat : repeat-x;
|
||||||
|
-webkit-mask-size : 50%; //Scale only X to fit page width, leave height at aspect ratio, designed to hang off the edge
|
||||||
|
-webkit-mask-position : 50% calc(50% - var(--offset));
|
||||||
|
mask-image : var(--wc);
|
||||||
|
mask-repeat : repeat-x;
|
||||||
|
mask-size : 50%;
|
||||||
|
mask-position : 50% calc(50% - var(--offset));
|
||||||
|
background-image : var(--checkerboard);
|
||||||
|
background-size : 20px;
|
||||||
|
z-index : -1;
|
||||||
|
transform : translateY(50%) translateX(-50%) rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||||
|
transition : transform 2s;
|
||||||
|
& > p:has(img) {
|
||||||
|
position : absolute;
|
||||||
|
width : 50%;
|
||||||
|
height : 50%;
|
||||||
|
bottom : 50%;
|
||||||
|
left : 50%;
|
||||||
|
transform : translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
|
||||||
|
transition : transform 2s;
|
||||||
|
}
|
||||||
|
& img {
|
||||||
|
position : absolute;
|
||||||
|
display : block;
|
||||||
|
bottom : 0;
|
||||||
|
}
|
||||||
|
&.bottom {
|
||||||
|
--rotation : 0;
|
||||||
|
& img {bottom: 0;}
|
||||||
|
}
|
||||||
|
&.top {
|
||||||
|
--rotation : 180;
|
||||||
|
& img {top: 0;}
|
||||||
|
}
|
||||||
|
&.left {
|
||||||
|
--rotation : 90;
|
||||||
|
& img {left: 0;}
|
||||||
|
}
|
||||||
|
&.right {
|
||||||
|
--rotation : -90;
|
||||||
|
& img {right: 0;}
|
||||||
|
}
|
||||||
|
&.revealImage {
|
||||||
|
--revealer : linear-gradient(0deg, rgba(0,0,0,.2) 0%, rgba(0,0,0,0.2));
|
||||||
|
--checkerboard : url(/assets/waterColorMasks/missingImage.png); //shows any masked regions not filled by image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageMaskEdge {
|
||||||
|
&1 { --wc : url(/assets/waterColorMasks/edge/0001.webp); }
|
||||||
|
&2 { --wc : url(/assets/waterColorMasks/edge/0002.webp); }
|
||||||
|
&3 { --wc : url(/assets/waterColorMasks/edge/0003.webp); }
|
||||||
|
&4 { --wc : url(/assets/waterColorMasks/edge/0004.webp); }
|
||||||
|
&5 { --wc : url(/assets/waterColorMasks/edge/0005.webp); }
|
||||||
|
&6 { --wc : url(/assets/waterColorMasks/edge/0006.webp); }
|
||||||
|
&7 { --wc : url(/assets/waterColorMasks/edge/0007.webp); }
|
||||||
|
&8 { --wc : url(/assets/waterColorMasks/edge/0008.webp); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="imageMaskCenter"] {
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
left : calc(var(--offsetX));
|
||||||
|
bottom : calc(var(--offsetY));
|
||||||
|
-webkit-mask-image : var(--wc), var(--revealer);
|
||||||
|
-webkit-mask-repeat : no-repeat;
|
||||||
|
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
|
-webkit-mask-position : 0% 0%;
|
||||||
|
mask-image : var(--wc), var(--revealer);
|
||||||
|
mask-repeat : no-repeat;
|
||||||
|
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
|
mask-position : 50% 50%;
|
||||||
|
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||||
|
|
||||||
|
& > p:has(img) {
|
||||||
|
position : absolute;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
bottom : 0;
|
||||||
|
left : 0;
|
||||||
|
transform : unset;
|
||||||
|
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
||||||
|
rotate(calc(-1deg * var(--rotation)))
|
||||||
|
translateX(calc(-1 * var(--offsetX)))
|
||||||
|
translateY(calc(1 * var(--offsetY)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageMaskCenter {
|
||||||
|
&1 { --wc : url(/assets/waterColorMasks/center/0001.webp); }
|
||||||
|
&2 { --wc : url(/assets/waterColorMasks/center/0002.webp); }
|
||||||
|
&3 { --wc : url(/assets/waterColorMasks/center/0003.webp); }
|
||||||
|
&4 { --wc : url(/assets/waterColorMasks/center/0004.webp); }
|
||||||
|
&5 { --wc : url(/assets/waterColorMasks/center/0005.webp); }
|
||||||
|
&6 { --wc : url(/assets/waterColorMasks/center/0006.webp); }
|
||||||
|
&7 { --wc : url(/assets/waterColorMasks/center/0007.webp); }
|
||||||
|
&8 { --wc : url(/assets/waterColorMasks/center/0008.webp); }
|
||||||
|
&9 { --wc : url(/assets/waterColorMasks/center/0009.webp); }
|
||||||
|
&10 { --wc : url(/assets/waterColorMasks/center/0010.webp); }
|
||||||
|
&11 { --wc : url(/assets/waterColorMasks/center/0011.webp); }
|
||||||
|
&12 { --wc : url(/assets/waterColorMasks/center/0012.webp); }
|
||||||
|
&13 { --wc : url(/assets/waterColorMasks/center/0013.webp); }
|
||||||
|
&14 { --wc : url(/assets/waterColorMasks/center/0014.webp); }
|
||||||
|
&15 { --wc : url(/assets/waterColorMasks/center/0015.webp); }
|
||||||
|
&16 { --wc : url(/assets/waterColorMasks/center/0016.webp); }
|
||||||
|
&special { --wc : url(/assets/waterColorMasks/center/special.webp); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[class*="imageMaskCorner"] {
|
||||||
|
height : 200%;
|
||||||
|
width : 200%;
|
||||||
|
left : calc(-50% + var(--offsetX));
|
||||||
|
bottom : calc(-50% + var(--offsetY));
|
||||||
|
-webkit-mask-image : var(--wc), var(--revealer);
|
||||||
|
-webkit-mask-repeat : no-repeat;
|
||||||
|
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
|
-webkit-mask-position : 50% 50%;
|
||||||
|
mask-image : var(--wc), var(--revealer);
|
||||||
|
mask-repeat : no-repeat;
|
||||||
|
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
|
mask-position : 50% 50%;
|
||||||
|
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||||
|
& > p:has(img) {
|
||||||
|
width : 50%;
|
||||||
|
height : 50%; //Complex transform below to handle mix of % and cm offsets
|
||||||
|
left : 25%;
|
||||||
|
bottom : 25%;
|
||||||
|
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
||||||
|
rotate(calc(-1deg * var(--rotation)))
|
||||||
|
translateX(calc(-1 * var(--offsetX)))
|
||||||
|
translateY(calc(1 * var(--offsetY)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.imageMaskCorner {
|
||||||
|
&1 { --wc : url(/assets/waterColorMasks/corner/0001.webp); }
|
||||||
|
&2 { --wc : url(/assets/waterColorMasks/corner/0002.webp); }
|
||||||
|
&3 { --wc : url(/assets/waterColorMasks/corner/0003.webp); }
|
||||||
|
&4 { --wc : url(/assets/waterColorMasks/corner/0004.webp); }
|
||||||
|
&5 { --wc : url(/assets/waterColorMasks/corner/0005.webp); }
|
||||||
|
&6 { --wc : url(/assets/waterColorMasks/corner/0006.webp); }
|
||||||
|
&7 { --wc : url(/assets/waterColorMasks/corner/0007.webp); }
|
||||||
|
&8 { --wc : url(/assets/waterColorMasks/corner/0008.webp); }
|
||||||
|
&9 { --wc : url(/assets/waterColorMasks/corner/0009.webp); }
|
||||||
|
&10 { --wc : url(/assets/waterColorMasks/corner/0010.webp); }
|
||||||
|
&11 { --wc : url(/assets/waterColorMasks/corner/0011.webp); }
|
||||||
|
&12 { --wc : url(/assets/waterColorMasks/corner/0012.webp); }
|
||||||
|
&13 { --wc : url(/assets/waterColorMasks/corner/0013.webp); }
|
||||||
|
&14 { --wc : url(/assets/waterColorMasks/corner/0014.webp); }
|
||||||
|
&15 { --wc : url(/assets/waterColorMasks/corner/0015.webp); }
|
||||||
|
&16 { --wc : url(/assets/waterColorMasks/corner/0016.webp); }
|
||||||
|
&17 { --wc : url(/assets/waterColorMasks/corner/0017.webp); }
|
||||||
|
&18 { --wc : url(/assets/waterColorMasks/corner/0018.webp); }
|
||||||
|
&19 { --wc : url(/assets/waterColorMasks/corner/0019.webp); }
|
||||||
|
&20 { --wc : url(/assets/waterColorMasks/corner/0020.webp); }
|
||||||
|
&21 { --wc : url(/assets/waterColorMasks/corner/0021.webp); }
|
||||||
|
&22 { --wc : url(/assets/waterColorMasks/corner/0022.webp); }
|
||||||
|
&23 { --wc : url(/assets/waterColorMasks/corner/0023.webp); }
|
||||||
|
&24 { --wc : url(/assets/waterColorMasks/corner/0024.webp); }
|
||||||
|
&25 { --wc : url(/assets/waterColorMasks/corner/0025.webp); }
|
||||||
|
&26 { --wc : url(/assets/waterColorMasks/corner/0026.webp); }
|
||||||
|
&27 { --wc : url(/assets/waterColorMasks/corner/0027.webp); }
|
||||||
|
&28 { --wc : url(/assets/waterColorMasks/corner/0028.webp); }
|
||||||
|
&29 { --wc : url(/assets/waterColorMasks/corner/0029.webp); }
|
||||||
|
&30 { --wc : url(/assets/waterColorMasks/corner/0030.webp); }
|
||||||
|
&31 { --wc : url(/assets/waterColorMasks/corner/0031.webp); }
|
||||||
|
&32 { --wc : url(/assets/waterColorMasks/corner/0032.webp); }
|
||||||
|
&33 { --wc : url(/assets/waterColorMasks/corner/0033.webp); }
|
||||||
|
&34 { --wc : url(/assets/waterColorMasks/corner/0034.webp); }
|
||||||
|
&35 { --wc : url(/assets/waterColorMasks/corner/0035.webp); }
|
||||||
|
&36 { --wc : url(/assets/waterColorMasks/corner/0036.webp); }
|
||||||
|
&37 { --wc : url(/assets/waterColorMasks/corner/0037.webp); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
themes/V3/Journal/dropdownPreview.png
Normal file
|
After Width: | Height: | Size: 809 KiB |
@@ -10,6 +10,11 @@
|
|||||||
@monsterBorderImage : url('/assets/monsterBorderFancy.png');
|
@monsterBorderImage : url('/assets/monsterBorderFancy.png');
|
||||||
@codeBorderImage : url('/assets/codeBorder.png');
|
@codeBorderImage : url('/assets/codeBorder.png');
|
||||||
@classTableDecoration : url('/assets/classTableDecoration.png');
|
@classTableDecoration : url('/assets/classTableDecoration.png');
|
||||||
|
@naturalCritLogo : url('/assets/naturalCritLogo.svg');
|
||||||
|
@coverPageBanner : url('/assets/coverPageBanner.svg');
|
||||||
|
@horizontalRule : url('/assets/horizontalRule.svg');
|
||||||
|
@insideCoverMask : url('/assets/insideCoverMask.png');
|
||||||
|
@scriptBorder : url('/assets/scriptBorder.png');
|
||||||
|
|
||||||
// Watercolor Images
|
// Watercolor Images
|
||||||
@watercolor1 : url('/assets/watercolor/watercolor1.png');
|
@watercolor1 : url('/assets/watercolor/watercolor1.png');
|
||||||
|
|||||||
1
themes/assets/coverPageBanner.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 970.25 154.67"><defs><style>.cls-1{fill:#ed1f24;}</style></defs><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="banner"><path id="mainShape" class="cls-1" d="M0,154.67V6.42l188.38,0s137.26,9.45,141.43,9.79c0,.14-96.52,6.76-96.52,6.76l47.09,2.24,39.48-3.05L479.43,34s-36.75,2.68-50.19,3.71c0,.29,178.19,14.48,178.19,14.48s34-3.95,34-4.24c-22.33-1.59-74.72-5.66-74.72-5.66l94-6.19s33.14,2.2,46.39,3.18c12.39.92,40.61,2.82,40.61,2.82l-24.34,2.07,35.24,3,47.36-3.62L762.5,40.26l95-6.94s84.83,6.11,96.45,7c0,.06-65.05,4.32-92.07,6.19-.29,0,39.6,3.57,54.16,4.73,0,.19-108.33,6.75-159.44,10.21,40.47,4.1,86.29,8.6,126,12.62,0,.15-41,4.34-58.14,6.16,0,.21,65.56,5.15,93.67,7.41-.2,0-41.27,2.79-56.81,4,0,.23,44.76,3.66,62.76,5.09,0,.11-103.55,7.17-150.95,10.53,0,.25,47.29,3.67,66.36,5.19,0,.1-62.19,4.45-89.84,6.47,0,.27,10.77,2.61,10.77,2.61L532.16,139.22,459.29,134l44.38-3.57L434.1,125l-58.43,4.31,59.76,4.36-123.38,11,44.19,3.14S337.9,149.34,330,150c-9.83.77-59.3,4.72-59.3,4.72Z"/><polygon id="diamondF" class="cls-1" points="552.6 154.33 469.43 147.19 550.72 142.21 633.24 148.52 552.6 154.33"/><polygon id="diamondE" class="cls-1" points="631.14 140.38 741.62 130.66 782.71 133.62 755.63 135.82 812.67 140.14 722.1 146.81 631.14 140.38"/><polygon id="diamondD" class="cls-1" points="812.57 62.87 892.92 57.13 970.25 63.1 894.05 68.76 812.57 62.87"/><path id="diamondC" class="cls-1" d="M480.21,29.59c4.21-.38,71.22-4.68,71.22-4.68l67.15,4.94-68.22,4.87S480.22,29.68,480.21,29.59Z"/><polygon id="diamondB" class="cls-1" points="450.19 23.52 344.58 16.3 449.17 8.62 556.38 16.36 450.19 23.52"/><polygon id="diamondA" class="cls-1" points="297.87 0 350.13 3.9 296.54 7.79 241.43 3.9 297.87 0"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
BIN
themes/assets/dragonBackground.png
Normal file
|
After Width: | Height: | Size: 366 KiB |
1
themes/assets/horizontalRule.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 762.29 18.4"><defs><style>.cls-1{fill:#ed1f24;}</style></defs><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="horizontalRule"><path id="mainShape" class="cls-1" d="M0,9.06S406.1,0,381.53,0,762.29,8.7,762.29,8.7s-350.49,10-381.53,9.69S0,9.06,0,9.06Z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 335 B |
BIN
themes/assets/insideCoverMask.png
Normal file
|
After Width: | Height: | Size: 262 KiB |
1
themes/assets/naturalCritLogo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 94.65 94.6"><defs><style>.cls-1{fill:#ed1f24;}</style></defs><title>NaturalCritLogo</title><g id="Layer_2" data-name="Layer 2"><g id="base"><path id="D20" class="cls-1" d="M63.45.09s-45.91,12.4-46,12.45a.71.71,0,0,0-.15.08l-.15.1-.12.11a1.07,1.07,0,0,0-.14.16l-.09.11-.12.23,0,.06L.2,54.9a1.59,1.59,0,0,0,.11,1.69L29.36,94h0l0,0,.08.08.08.08.09.09.08.06.13.07a0,0,0,0,0,0,0,1.59,1.59,0,0,0,.27.12l.13.05.06,0a1.55,1.55,0,0,0,.37,0,1.63,1.63,0,0,0,.31,0l45.67-8.3.16,0,.11,0,.12,0,.06,0s0,0,0,0l.06,0a1.65,1.65,0,0,0,.36-.28l0-.06a1.6,1.6,0,0,0,.26-.38s0,0,0,0v0h0a.14.14,0,0,1,0-.06L94.52,43.74a1.4,1.4,0,0,0,.11-.4.41.41,0,0,0,0-.11,1.13,1.13,0,0,0,0-.26.66.66,0,0,0,0-.14,2,2,0,0,0-.06-.26l0-.11a2.68,2.68,0,0,0-.18-.33v0L65.29.6C64.77-.31,63.45.09,63.45.09ZM74.9,81.7l-28.81-18L78.5,38.49ZM44.1,61l-11-40.17L77,35.39ZM82,37.78l8.92,5.95L79,73.48Zm4.46-1.1-4.6-3.06L75.69,21.36Zm-9.26-4.8-42.07-14,28.05-14ZM30.56,16.34l-6.49-2.16L47.85,7.7Zm-11.35-.21L27.88,19,7.64,45Zm10.73,5.76L40.78,61.64,4.64,54.42Zm10.82,43.2L30.26,89.6,5.75,58.09Zm3.16,1.24L71.74,83.72l-38.26,7Z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
themes/assets/scriptBorder.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
themes/assets/waterColorMasks/center/0001.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
themes/assets/waterColorMasks/center/0002.webp
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
themes/assets/waterColorMasks/center/0003.webp
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
themes/assets/waterColorMasks/center/0004.webp
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
themes/assets/waterColorMasks/center/0005.webp
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
themes/assets/waterColorMasks/center/0006.webp
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
themes/assets/waterColorMasks/center/0007.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
themes/assets/waterColorMasks/center/0008.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
themes/assets/waterColorMasks/center/0009.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
themes/assets/waterColorMasks/center/0010.webp
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
themes/assets/waterColorMasks/center/0011.webp
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
themes/assets/waterColorMasks/center/0012.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
themes/assets/waterColorMasks/center/0013.webp
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
themes/assets/waterColorMasks/center/0014.webp
Normal file
|
After Width: | Height: | Size: 57 KiB |