mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-07 05:32:41 +00:00
Merge branch 'master' into Issue_1958
This commit is contained in:
@@ -64,6 +64,12 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Test - Mustache Spans
|
name: Test - Mustache Spans
|
||||||
command: npm run test:mustache-syntax
|
command: npm run test:mustache-syntax
|
||||||
|
- run:
|
||||||
|
name: Test - Definition Lists
|
||||||
|
command: npm run test:definition-lists
|
||||||
|
- run:
|
||||||
|
name: Test - Variables
|
||||||
|
command: npm run test:variables
|
||||||
- run:
|
- run:
|
||||||
name: Test - Routes
|
name: Test - Routes
|
||||||
command: npm run test:route
|
command: npm run test:route
|
||||||
|
|||||||
103
.github/actions/limit-pull-requests/action.yml
vendored
Normal file
103
.github/actions/limit-pull-requests/action.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
name: Limit pull requests
|
||||||
|
description: >
|
||||||
|
Limit the number of open pull requests to the repository created by a user
|
||||||
|
author: ZhongRuoyu (from Homebrew repository)
|
||||||
|
branding:
|
||||||
|
icon: alert-triangle
|
||||||
|
color: yellow
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
token:
|
||||||
|
description: GitHub token
|
||||||
|
required: false
|
||||||
|
default: ${{ github.token }}
|
||||||
|
except-users:
|
||||||
|
description: The users exempted from the limit, one per line
|
||||||
|
required: false
|
||||||
|
# https://docs.github.com/en/graphql/reference/enums#commentauthorassociation
|
||||||
|
except-author-associations:
|
||||||
|
description: The author associations exempted from the limit, one per line
|
||||||
|
required: false
|
||||||
|
comment-limit:
|
||||||
|
description: >
|
||||||
|
Post the comment when the user's number of open pull requests exceeds this
|
||||||
|
number and `comment` is not empty
|
||||||
|
required: true
|
||||||
|
default: "10"
|
||||||
|
comment:
|
||||||
|
description: The comment to post when the limit is reached
|
||||||
|
required: false
|
||||||
|
close-limit:
|
||||||
|
description: >
|
||||||
|
Close the pull request when the user's number of open pull requests
|
||||||
|
exceeds this number and `close` is set to `true`
|
||||||
|
required: true
|
||||||
|
default: "50"
|
||||||
|
close:
|
||||||
|
description: Whether to close the pull request when the limit is reached
|
||||||
|
required: true
|
||||||
|
default: "false"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Check the number of pull requests
|
||||||
|
id: count-pull-requests
|
||||||
|
run: |
|
||||||
|
# If the user is exempted, assume they have no pull requests.
|
||||||
|
if grep -Fiqx '${{ github.actor }}' <<<"$EXCEPT_USERS"; then
|
||||||
|
echo "::notice::@${{ github.actor }} is exempted from the limit."
|
||||||
|
echo "count=0" >>"$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if grep -Fiqx '${{ github.event.pull_request.author_association }}' <<<"$EXCEPT_AUTHOR_ASSOCIATIONS"; then
|
||||||
|
echo "::notice::@{{ github.actor }} is a ${{ github.event.pull_request.author_association }} exempted from the limit."
|
||||||
|
echo "count=0" >>"$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
count="$(
|
||||||
|
gh api \
|
||||||
|
--method GET \
|
||||||
|
--header 'Accept: application/vnd.github+json' \
|
||||||
|
--header 'X-GitHub-Api-Version: 2022-11-28' \
|
||||||
|
--field state=open \
|
||||||
|
--paginate \
|
||||||
|
'/repos/{owner}/{repo}/pulls' |
|
||||||
|
jq \
|
||||||
|
--raw-output \
|
||||||
|
--arg USER '${{ github.actor }}' \
|
||||||
|
'map(select(.user.login == $USER)) | length'
|
||||||
|
)"
|
||||||
|
echo "::notice::@${{ github.actor }} has $count open pull request(s)."
|
||||||
|
echo "count=$count" >>"$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
GH_TOKEN: ${{ inputs.token }}
|
||||||
|
EXCEPT_USERS: ${{ inputs.except-users }}
|
||||||
|
EXCEPT_AUTHOR_ASSOCIATIONS: ${{ inputs.except-author-associations }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Comment on pull request
|
||||||
|
if: >
|
||||||
|
fromJSON(steps.count-pull-requests.outputs.count) > fromJSON(inputs.comment-limit) &&
|
||||||
|
inputs.comment != ''
|
||||||
|
run: |
|
||||||
|
gh pr comment '${{ github.event.pull_request.number }}' \
|
||||||
|
--body="${COMMENT_BODY}"
|
||||||
|
env:
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
GH_TOKEN: ${{ inputs.token }}
|
||||||
|
COMMENT_BODY: ${{ inputs.comment }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Close pull request
|
||||||
|
if: >
|
||||||
|
fromJSON(steps.count-pull-requests.outputs.count) > fromJSON(inputs.close-limit) &&
|
||||||
|
inputs.close == 'true'
|
||||||
|
run: |
|
||||||
|
gh pr close '${{ github.event.pull_request.number }}'
|
||||||
|
env:
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
GH_TOKEN: ${{ inputs.token }}
|
||||||
|
shell: bash
|
||||||
29
.github/workflows/pr-check.yml
vendored
Normal file
29
.github/workflows/pr-check.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: PR Check
|
||||||
|
on: pull_request_target
|
||||||
|
env:
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
GH_NO_UPDATE_NOTIFIER: 1
|
||||||
|
GH_PROMPT_DISABLED: 1
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
statuses: write
|
||||||
|
jobs:
|
||||||
|
limit-pull-requests:
|
||||||
|
if: always() && github.repository_owner == 'naturalcrit'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name : Run limit-pull-requests action
|
||||||
|
uses: ./.github/actions/limit-pull-requests
|
||||||
|
with:
|
||||||
|
except-users: |
|
||||||
|
dependabot
|
||||||
|
comment-limit: 3
|
||||||
|
comment: |
|
||||||
|
Hi, thanks for your contribution to the Homebrewery! You already have >=3 open pull requests. Consider completing some of your existing PRs before opening new ones. Thanks!
|
||||||
|
close-limit: 5
|
||||||
|
close: false
|
||||||
73
changelog.md
73
changelog.md
@@ -84,7 +84,70 @@ 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).
|
||||||
|
|
||||||
### Wednesday 21/2/2024 - v3.11.0
|
### Monday 18/3/2024 - v3.12.0
|
||||||
|
{{taskList
|
||||||
|
|
||||||
|
##### 5e-Cleric
|
||||||
|
|
||||||
|
* [x] Fix language-specific hyphenation on print page
|
||||||
|
|
||||||
|
Fixes issue [#3294](https://github.com/naturalcrit/homebrewery/issues/3294)
|
||||||
|
|
||||||
|
* [x] Upgrade Font-Awesome to v6.51
|
||||||
|
|
||||||
|
* [x] Allow downloaded files to be uploaded via {{openSans **NEW {{fa,fa-plus-square}} → FROM UPLOAD {{fa,fa-upload}}**}}
|
||||||
|
|
||||||
|
##### G-Ambatte
|
||||||
|
|
||||||
|
* [x] Fix an edge case crash with empty documents
|
||||||
|
|
||||||
|
Fixes issue [#3315](https://github.com/naturalcrit/homebrewery/issues/3315)
|
||||||
|
|
||||||
|
* [x] Brews on the user page can be searched by tag; clicking a tag adds it to the filter
|
||||||
|
|
||||||
|
Fixes issue [#3164](https://github.com/naturalcrit/homebrewery/issues/3164)
|
||||||
|
|
||||||
|
* [x] Add *DiceFont* icons {{df,d20-20}} `{{df,icon-name}}`
|
||||||
|
|
||||||
|
##### abquintic
|
||||||
|
|
||||||
|
* [x] Fix ^super^ and ^^sub^^ highlighting in the text editor
|
||||||
|
|
||||||
|
* [x] Add new syntax for multiline Definition Lists:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Term
|
||||||
|
::Definition 1
|
||||||
|
::Definition 2
|
||||||
|
with more text
|
||||||
|
```
|
||||||
|
|
||||||
|
produces:
|
||||||
|
|
||||||
|
Term
|
||||||
|
::Definition 1
|
||||||
|
::Definition 2
|
||||||
|
with more text
|
||||||
|
|
||||||
|
Fixes issue [#2340](https://github.com/naturalcrit/homebrewery/issues/2340)
|
||||||
|
|
||||||
|
##### RKuerten :
|
||||||
|
* [x] Fix monster stat block backgrounds on print page
|
||||||
|
|
||||||
|
Fixes issue [#3275](https://github.com/naturalcrit/homebrewery/issues/3275)
|
||||||
|
|
||||||
|
* [x] Added new text editor theme: "Darkvision".
|
||||||
|
|
||||||
|
##### calculuschild, G-Ambatte, 5e-Cleric
|
||||||
|
|
||||||
|
* [x] Codebase and UI cleanup
|
||||||
|
}}
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
|
|
||||||
|
### Friday 21/2/2024 - v3.11.0
|
||||||
{{taskList
|
{{taskList
|
||||||
|
|
||||||
##### Gazook89
|
##### Gazook89
|
||||||
@@ -166,14 +229,16 @@ Fixes issue [1488](https://github.com/naturalcrit/homebrewery/issues/1488)
|
|||||||
Fixes issues [2510](https://github.com/naturalcrit/homebrewery/issues/2510),
|
Fixes issues [2510](https://github.com/naturalcrit/homebrewery/issues/2510),
|
||||||
[2975](https://github.com/naturalcrit/homebrewery/issues/2975)
|
[2975](https://github.com/naturalcrit/homebrewery/issues/2975)
|
||||||
|
|
||||||
* [x] New Variables syntax. See below for details.
|
* [x] Brew Variables
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
\
|
||||||
|
|
||||||
{{wide
|
{{wide
|
||||||
|
|
||||||
### Brew Variable Syntax
|
### Brew Variable Syntax
|
||||||
|
|
||||||
You may already be familiar with `[link](url)` and `` syntax. We have expanded this to include a third `$[variable](text)` syntax. All three of these syntaxes now share a common set of features:
|
You may already be familiar with `[link](url)` and `` synax. We have expanded this to include a third `$[variable](text)` syntax. All three of these syntaxes now share a common set of features:
|
||||||
|
|
||||||
{{varSyntaxTable
|
{{varSyntaxTable
|
||||||
| syntax | description |
|
| syntax | description |
|
||||||
@@ -1512,7 +1577,7 @@ myStyle {color: black}
|
|||||||
### Sunday, 29/05/2016 - v2.1.0
|
### Sunday, 29/05/2016 - v2.1.0
|
||||||
- Finally added a syntax for doing spell lists. A bit in-depth about why this took so long. Essentially I'm running out of syntax to use in stardard Markdown. There are too many unique elements in the PHB-style to be mapped. I solved this earlier by stacking certain elements together (eg. an `<hr>` before a `blockquote` turns it into moster state block), but those are getting unweildly. I would like to simply wrap these in `div`s with classes, but unfortunately Markdown stops processing when within HTML blocks. To get around this I wrote my own override to the Markdown parser and lexer to process Markdown within a simple div class wrapper. This should open the door for more unique syntaxes in the future. Big step!
|
- Finally added a syntax for doing spell lists. A bit in-depth about why this took so long. Essentially I'm running out of syntax to use in stardard Markdown. There are too many unique elements in the PHB-style to be mapped. I solved this earlier by stacking certain elements together (eg. an `<hr>` before a `blockquote` turns it into moster state block), but those are getting unweildly. I would like to simply wrap these in `div`s with classes, but unfortunately Markdown stops processing when within HTML blocks. To get around this I wrote my own override to the Markdown parser and lexer to process Markdown within a simple div class wrapper. This should open the door for more unique syntaxes in the future. Big step!
|
||||||
- Override Ctrl+P (and cmd+P) to launch to the print page. Many people try to just print either the editing or share page to get a PDF. While this dones;t make much sense, I do get a ton of issues about it. So now if you try to do this, it'll just bring you imediately to the print page. Everybody wins!
|
- Override Ctrl+P (and cmd+P) to launch to the print page. Many people try to just print either the editing or share page to get a PDF. While this dones;t make much sense, I do get a ton of issues about it. So now if you try to do this, it'll just bring you imediately to the print page. Everybody wins!
|
||||||
- The onboarding flow has also been confusing a few users (Homepage -> new -> save -> edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
|
- The onboarding flow has also been confusing a few users (Homepage → new → save → edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
|
||||||
- Added a 'Recently Edited' and 'Recently Viewed' nav item to the edit and share page respectively. Each will remember the last 8 items you edited or viewed and when you viewed it. Makes use of the new title attribute of brews to easy navigatation.
|
- Added a 'Recently Edited' and 'Recently Viewed' nav item to the edit and share page respectively. Each will remember the last 8 items you edited or viewed and when you viewed it. Makes use of the new title attribute of brews to easy navigatation.
|
||||||
- Paragraphs now indent properly after lists (thanks u/slitjen!)
|
- Paragraphs now indent properly after lists (thanks u/slitjen!)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,28 @@
|
|||||||
box-shadow : 1px 4px 14px #000000;
|
box-shadow : 1px 4px 14px #000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 20px;
|
||||||
|
&:horizontal{
|
||||||
|
height: 20px;
|
||||||
|
width:auto;
|
||||||
|
}
|
||||||
|
&-thumb {
|
||||||
|
background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px);
|
||||||
|
&:horizontal{
|
||||||
|
background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-corner {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.pane { position : relative; }
|
.pane { position : relative; }
|
||||||
.pageInfo {
|
.pageInfo {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const createClass = require('create-react-class');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
|
const Markdown = require('../../../shared/naturalcrit/markdown.js');
|
||||||
|
|
||||||
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
|
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
|
||||||
const SnippetBar = require('./snippetbar/snippetbar.jsx');
|
const SnippetBar = require('./snippetbar/snippetbar.jsx');
|
||||||
@@ -81,7 +82,7 @@ const Editor = createClass({
|
|||||||
updateEditorSize : function() {
|
updateEditorSize : function() {
|
||||||
if(this.refs.codeEditor) {
|
if(this.refs.codeEditor) {
|
||||||
let paneHeight = this.refs.main.parentNode.clientHeight;
|
let paneHeight = this.refs.main.parentNode.clientHeight;
|
||||||
paneHeight -= SNIPPETBAR_HEIGHT + 1;
|
paneHeight -= SNIPPETBAR_HEIGHT;
|
||||||
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
|
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -151,12 +152,19 @@ const Editor = createClass({
|
|||||||
|
|
||||||
// definition lists
|
// definition lists
|
||||||
if(line.includes('::')){
|
if(line.includes('::')){
|
||||||
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
|
if(/^:*$/.test(line) == true){ return };
|
||||||
|
const regex = /^([^\n]*?:?\s?)(::[^\n]*)(?:\n|$)/ymd; // the `d` flag, for match indices, throws an ESLint error.
|
||||||
let match;
|
let match;
|
||||||
while ((match = regex.exec(line)) != null){
|
while ((match = regex.exec(line)) != null){
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[0]) }, { line: lineNumber, ch: line.indexOf(match[0]) + match[0].length }, { className: 'define' });
|
codeMirror.markText({ line: lineNumber, ch: match.indices[0][0] }, { line: lineNumber, ch: match.indices[0][1] }, { className: 'dl-highlight' });
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'term' });
|
codeMirror.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' });
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[2]) }, { line: lineNumber, ch: line.indexOf(match[2]) + match[2].length }, { className: 'definition' });
|
codeMirror.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' });
|
||||||
|
const ddIndex = match.indices[2][0];
|
||||||
|
let colons = /::/g;
|
||||||
|
let colonMatches = colons.exec(match[2]);
|
||||||
|
if(colonMatches !== null){
|
||||||
|
codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight'} )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +220,34 @@ const Editor = createClass({
|
|||||||
endCh = match.index+match[0].length;
|
endCh = match.index+match[0].length;
|
||||||
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
|
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emojis
|
||||||
|
if(line.match(/:[^\s:]+:/g)) {
|
||||||
|
let startIndex = line.indexOf(':');
|
||||||
|
const emojiRegex = /:[^\s:]+:/gy;
|
||||||
|
|
||||||
|
while (startIndex >= 0) {
|
||||||
|
emojiRegex.lastIndex = startIndex;
|
||||||
|
let match = emojiRegex.exec(line);
|
||||||
|
if (match) {
|
||||||
|
let tokens = Markdown.marked.lexer(match[0]);
|
||||||
|
tokens = tokens[0].tokens.filter(t => t.type == 'emoji')
|
||||||
|
if (!tokens.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let startPos = { line: lineNumber, ch: match.index };
|
||||||
|
let endPos = { line: lineNumber, ch: match.index + match[0].length };
|
||||||
|
|
||||||
|
// Iterate over conflicting marks and clear them
|
||||||
|
var marks = codeMirror.findMarks(startPos, endPos);
|
||||||
|
marks.forEach(function(marker) {
|
||||||
|
marker.clear();
|
||||||
|
});
|
||||||
|
codeMirror.markText(startPos, endPos, { className: 'emoji' });
|
||||||
|
}
|
||||||
|
startIndex = line.indexOf(':', Math.max(startIndex + 1, emojiRegex.lastIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,6 +43,16 @@
|
|||||||
font-weight : bold;
|
font-weight : bold;
|
||||||
color : green;
|
color : green;
|
||||||
}
|
}
|
||||||
|
.emoji:not(.cm-comment) {
|
||||||
|
margin-left : 2px;
|
||||||
|
color : #360034;
|
||||||
|
background : #ffc8ff;
|
||||||
|
border-radius : 6px;
|
||||||
|
font-weight : bold;
|
||||||
|
padding-bottom : 1px;
|
||||||
|
outline-offset : -2px;
|
||||||
|
outline : solid 2px #ff96fc;
|
||||||
|
}
|
||||||
.superscript:not(.cm-comment) {
|
.superscript:not(.cm-comment) {
|
||||||
font-weight : bold;
|
font-weight : bold;
|
||||||
color : goldenrod;
|
color : goldenrod;
|
||||||
@@ -55,6 +65,16 @@
|
|||||||
vertical-align : sub;
|
vertical-align : sub;
|
||||||
font-size : 0.9em;
|
font-size : 0.9em;
|
||||||
}
|
}
|
||||||
|
.dl-highlight {
|
||||||
|
&.dl-colon-highlight {
|
||||||
|
font-weight : bold;
|
||||||
|
color : #949494;
|
||||||
|
background : #E5E5E5;
|
||||||
|
border-radius : 3px;
|
||||||
|
}
|
||||||
|
&.dt-highlight { color : rgb(96, 117, 143); }
|
||||||
|
&.dd-highlight { color : rgb(97, 57, 178); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.brewJump {
|
.brewJump {
|
||||||
|
|||||||
@@ -130,6 +130,8 @@
|
|||||||
height : 1.2em;
|
height : 1.2em;
|
||||||
margin-right : 8px;
|
margin-right : 8px;
|
||||||
font-size : 1.2em;
|
font-size : 1.2em;
|
||||||
|
min-width: 25px;
|
||||||
|
text-align: center;
|
||||||
& ~ i {
|
& ~ i {
|
||||||
margin-right : 0;
|
margin-right : 0;
|
||||||
margin-left : 5px;
|
margin-left : 5px;
|
||||||
@@ -138,7 +140,7 @@
|
|||||||
&.font {
|
&.font {
|
||||||
height : auto;
|
height : auto;
|
||||||
&::before {
|
&::before {
|
||||||
font-size : 1.4em;
|
font-size : 1em;
|
||||||
content : 'ABC';
|
content : 'ABC';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const Homebrew = createClass({
|
|||||||
<Route path='/print' element={<WithRoute el={PrintPage} />} />
|
<Route path='/print' element={<WithRoute el={PrintPage} />} />
|
||||||
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
|
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
|
||||||
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
|
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
|
||||||
<Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} uiItems={this.props.brew.uiItems} />} />
|
<Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} accountDetails={this.props.brew.accountDetails} />} />
|
||||||
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
||||||
<Route path='/error' element={<WithRoute el={ErrorPage} brew={this.props.brew} />} />
|
<Route path='/error' element={<WithRoute el={ErrorPage} brew={this.props.brew} />} />
|
||||||
<Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
<Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
||||||
|
|||||||
@@ -15,6 +15,23 @@
|
|||||||
}
|
}
|
||||||
&.listPage .content {
|
&.listPage .content {
|
||||||
overflow-y : scroll;
|
overflow-y : scroll;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 20px;
|
||||||
|
&:horizontal{
|
||||||
|
height: 20px;
|
||||||
|
width:auto;
|
||||||
|
}
|
||||||
|
&-thumb {
|
||||||
|
background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px);
|
||||||
|
&:horizontal{
|
||||||
|
background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-corner {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,102 +1,82 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const cx = require('classnames');
|
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
|
||||||
|
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
|
||||||
const Account = require('../../navbar/account.navitem.jsx');
|
|
||||||
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
|
||||||
|
|
||||||
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
||||||
|
|
||||||
let SAVEKEY = '';
|
let SAVEKEY = '';
|
||||||
|
|
||||||
const AccountPage = createClass({
|
const AccountPage = (props)=>{
|
||||||
displayName : 'AccountPage',
|
// destructure props and set state for save location
|
||||||
getDefaultProps : function() {
|
const { accountDetails, brew } = props;
|
||||||
return {
|
const [saveLocation, setSaveLocation] = React.useState('');
|
||||||
brew : {},
|
|
||||||
uiItems : {}
|
// initialize save location from local storage based on user id
|
||||||
};
|
React.useEffect(()=>{
|
||||||
},
|
if(!saveLocation && accountDetails.username) {
|
||||||
getInitialState : function() {
|
SAVEKEY = `HOMEBREWERY-DEFAULT-SAVE-LOCATION-${accountDetails.username}`;
|
||||||
return {
|
// if no SAVEKEY in local storage, default save location to Google Drive if user has Google account.
|
||||||
uiItems : this.props.uiItems
|
let saveLocation = window.localStorage.getItem(SAVEKEY);
|
||||||
};
|
saveLocation = saveLocation ?? (accountDetails.googleId ? 'GOOGLE-DRIVE' : 'HOMEBREWERY');
|
||||||
},
|
setActiveSaveLocation(saveLocation);
|
||||||
componentDidMount : function(){
|
|
||||||
if(!this.state.saveLocation && this.props.uiItems.username) {
|
|
||||||
SAVEKEY = `HOMEBREWERY-DEFAULT-SAVE-LOCATION-${this.props.uiItems.username}`;
|
|
||||||
let saveLocation = window.localStorage.getItem(SAVEKEY);
|
|
||||||
saveLocation = saveLocation ?? (this.state.uiItems.googleId ? 'GOOGLE-DRIVE' : 'HOMEBREWERY');
|
|
||||||
this.makeActive(saveLocation);
|
|
||||||
}
|
}
|
||||||
},
|
}, []);
|
||||||
|
|
||||||
makeActive : function(newSelection){
|
const setActiveSaveLocation = (newSelection)=>{
|
||||||
if(this.state.saveLocation == newSelection) return;
|
if(saveLocation === newSelection) return;
|
||||||
window.localStorage.setItem(SAVEKEY, newSelection);
|
window.localStorage.setItem(SAVEKEY, newSelection);
|
||||||
this.setState({
|
setSaveLocation(newSelection);
|
||||||
saveLocation : newSelection
|
};
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
renderButton : function(name, key, shouldRender=true){
|
// todo: should this be a set of radio buttons (well styled) since it's either/or choice?
|
||||||
if(!shouldRender) return;
|
const renderSaveLocationButton = (name, key, shouldRender = true)=>{
|
||||||
return <button className={this.state.saveLocation==key ? 'active' : ''} onClick={()=>{this.makeActive(key);}}>{name}</button>;
|
if(!shouldRender) return null;
|
||||||
},
|
return (
|
||||||
|
<button className={saveLocation === key ? 'active' : ''} onClick={()=>{setActiveSaveLocation(key);}}>
|
||||||
|
{name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
renderNavItems : function() {
|
// render the entirety of the account page content
|
||||||
return <Navbar>
|
const renderAccountPage = ()=>{
|
||||||
<Nav.section>
|
return (
|
||||||
<NewBrew />
|
<>
|
||||||
<HelpNavItem />
|
<div className='dataGroup'>
|
||||||
<RecentNavItem />
|
<h1>Account Information <i className='fas fa-user'></i></h1>
|
||||||
<Account />
|
<p><strong>Username: </strong>{accountDetails.username || 'No user currently logged in'}</p>
|
||||||
</Nav.section>
|
<p><strong>Last Login: </strong>{moment(accountDetails.issued).format('dddd, MMMM Do YYYY, h:mm:ss a ZZ') || '-'}</p>
|
||||||
</Navbar>;
|
</div>
|
||||||
},
|
<div className='dataGroup'>
|
||||||
|
<h3>Homebrewery Information <NaturalCritIcon /></h3>
|
||||||
|
<p><strong>Brews on Homebrewery: </strong>{accountDetails.mongoCount}</p>
|
||||||
|
</div>
|
||||||
|
<div className='dataGroup'>
|
||||||
|
<h3>Google Information <i className='fab fa-google-drive'></i></h3>
|
||||||
|
<p><strong>Linked to Google: </strong>{accountDetails.googleId ? 'YES' : 'NO'}</p>
|
||||||
|
{accountDetails.googleId && (
|
||||||
|
<p>
|
||||||
|
<strong>Brews on Google Drive: </strong>{accountDetails.googleCount ?? (
|
||||||
|
<>
|
||||||
|
Unable to retrieve files - <a href='https://github.com/naturalcrit/homebrewery/discussions/1580'>follow these steps to renew your Google credentials.</a>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className='dataGroup'>
|
||||||
|
<h4>Default Save Location</h4>
|
||||||
|
{renderSaveLocationButton('Homebrewery', 'HOMEBREWERY')}
|
||||||
|
{renderSaveLocationButton('Google Drive', 'GOOGLE-DRIVE', accountDetails.googleId)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
renderUiItems : function() {
|
// return the account page inside the base layout wrapper (with navbar etc).
|
||||||
return <>
|
return (
|
||||||
<div className='dataGroup'>
|
<UIPage brew={brew}>
|
||||||
<h1>Account Information <i className='fas fa-user'></i></h1>
|
{renderAccountPage()}
|
||||||
<p><strong>Username: </strong> {this.props.uiItems.username || 'No user currently logged in'}</p>
|
</UIPage>);
|
||||||
<p><strong>Last Login: </strong> {moment(this.props.uiItems.issued).format('dddd, MMMM Do YYYY, h:mm:ss a ZZ') || '-'}</p>
|
};
|
||||||
</div>
|
|
||||||
<div className='dataGroup'>
|
|
||||||
<h3>Homebrewery Information <NaturalCritIcon /></h3>
|
|
||||||
<p><strong>Brews on Homebrewery: </strong> {this.props.uiItems.mongoCount}</p>
|
|
||||||
</div>
|
|
||||||
<div className='dataGroup'>
|
|
||||||
<h3>Google Information <i className='fab fa-google-drive'></i></h3>
|
|
||||||
<p><strong>Linked to Google: </strong> {this.props.uiItems.googleId ? 'YES' : 'NO'}</p>
|
|
||||||
{this.props.uiItems.googleId &&
|
|
||||||
<p>
|
|
||||||
<strong>Brews on Google Drive: </strong> {this.props.uiItems.googleCount ?? <>Unable to retrieve files - <a href='https://github.com/naturalcrit/homebrewery/discussions/1580'>follow these steps to renew your Google credentials.</a></>}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className='dataGroup'>
|
|
||||||
<h4>Default Save Location</h4>
|
|
||||||
{this.renderButton('Homebrewery', 'HOMEBREWERY')}
|
|
||||||
{this.renderButton('Google Drive', 'GOOGLE-DRIVE', this.state.uiItems.googleId)}
|
|
||||||
</div>
|
|
||||||
</>;
|
|
||||||
},
|
|
||||||
|
|
||||||
render : function(){
|
|
||||||
return <UIPage brew={this.props.brew}>
|
|
||||||
{this.renderUiItems()}
|
|
||||||
</UIPage>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = AccountPage;
|
module.exports = AccountPage;
|
||||||
|
|||||||
@@ -1,41 +1,25 @@
|
|||||||
require('./errorPage.less');
|
require('./errorPage.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
||||||
const _ = require('lodash');
|
const Markdown = require('../../../../shared/naturalcrit/markdown.js');
|
||||||
const cx = require('classnames');
|
|
||||||
|
|
||||||
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
|
||||||
|
|
||||||
const Markdown = require('../../../../shared/naturalcrit/markdown.js');
|
|
||||||
|
|
||||||
const ErrorIndex = require('./errors/errorIndex.js');
|
const ErrorIndex = require('./errors/errorIndex.js');
|
||||||
|
|
||||||
const ErrorPage = createClass({
|
const ErrorPage = ({ brew })=>{
|
||||||
displayName : 'ErrorPage',
|
// Retrieving the error text based on the brew's error code from ErrorIndex
|
||||||
|
const errorText = ErrorIndex({ brew })[brew.HBErrorCode.toString()] || '';
|
||||||
|
|
||||||
getDefaultProps : function() {
|
return (
|
||||||
return {
|
<UIPage brew={{ title: 'Crit Fail!' }}>
|
||||||
ver : '0.0.0',
|
|
||||||
errorId : '',
|
|
||||||
text : '# Oops \n We could not find a brew with that id. **Sorry!**',
|
|
||||||
error : {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render : function(){
|
|
||||||
const errorText = ErrorIndex(this.props)[this.props.brew.HBErrorCode.toString()] || '';
|
|
||||||
|
|
||||||
return <UIPage brew={{ title: 'Crit Fail!' }}>
|
|
||||||
<div className='dataGroup'>
|
<div className='dataGroup'>
|
||||||
<div className='errorTitle'>
|
<div className='errorTitle'>
|
||||||
<h1>{`Error ${this.props.brew.status || '000'}`}</h1>
|
<h1>{`Error ${brew?.status || '000'}`}</h1>
|
||||||
<h4>{this.props.brew.text || 'No error text'}</h4>
|
<h4>{brew?.text || 'No error text'}</h4>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div dangerouslySetInnerHTML={{ __html: Markdown.render(errorText) }} />
|
<div dangerouslySetInnerHTML={{ __html: Markdown.render(errorText) }} />
|
||||||
</div>
|
</div>
|
||||||
</UIPage>;
|
</UIPage>
|
||||||
}
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = ErrorPage;
|
module.exports = ErrorPage;
|
||||||
|
|||||||
@@ -73,9 +73,11 @@ const errorIndex = (props)=>{
|
|||||||
**Properties** tab, and adding your username to the "invited authors" list. You can
|
**Properties** tab, and adding your username to the "invited authors" list. You can
|
||||||
then try to access this document again.
|
then try to access this document again.
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
**Brew Title:** ${props.brew.brewTitle || 'Unable to show title'}
|
**Brew Title:** ${props.brew.brewTitle || 'Unable to show title'}
|
||||||
|
|
||||||
**Current Authors:** ${props.brew.authors?.map((author)=>{return `${author}`;}).join(', ') || 'Unable to list authors'}
|
**Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'}
|
||||||
|
|
||||||
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
||||||
|
|
||||||
@@ -86,9 +88,14 @@ const errorIndex = (props)=>{
|
|||||||
You must be logged in to one of the accounts listed as an author of this brew.
|
You must be logged in to one of the accounts listed as an author of this brew.
|
||||||
User is not logged in. Please log in [here](${loginUrl}).
|
User is not logged in. Please log in [here](${loginUrl}).
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
**Brew Title:** ${props.brew.brewTitle || 'Unable to show title'}
|
**Brew Title:** ${props.brew.brewTitle || 'Unable to show title'}
|
||||||
|
|
||||||
**Current Authors:** ${props.brew.authors?.map((author)=>{return `${author}`;}).join(', ') || 'Unable to list authors'}`,
|
**Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'}
|
||||||
|
|
||||||
|
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
||||||
|
|
||||||
|
|
||||||
// Brew load error
|
// Brew load error
|
||||||
'05' : dedent`
|
'05' : dedent`
|
||||||
@@ -97,6 +104,8 @@ const errorIndex = (props)=>{
|
|||||||
The server could not locate the Homebrewery document. It was likely deleted by
|
The server could not locate the Homebrewery document. It was likely deleted by
|
||||||
its owner.
|
its owner.
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
**Requested access:** ${props.brew.accessType}
|
**Requested access:** ${props.brew.accessType}
|
||||||
|
|
||||||
**Brew ID:** ${props.brew.brewId}`,
|
**Brew ID:** ${props.brew.brewId}`,
|
||||||
@@ -113,6 +122,8 @@ const errorIndex = (props)=>{
|
|||||||
|
|
||||||
An error occurred while attempting to remove the Homebrewery document.
|
An error occurred while attempting to remove the Homebrewery document.
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
**Brew ID:** ${props.brew.brewId}`,
|
**Brew ID:** ${props.brew.brewId}`,
|
||||||
|
|
||||||
// Author delete error
|
// Author delete error
|
||||||
@@ -121,7 +132,21 @@ const errorIndex = (props)=>{
|
|||||||
|
|
||||||
An error occurred while attempting to remove the user from the Homebrewery document author list!
|
An error occurred while attempting to remove the user from the Homebrewery document author list!
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
**Brew ID:** ${props.brew.brewId}`,
|
**Brew ID:** ${props.brew.brewId}`,
|
||||||
|
|
||||||
|
// Brew locked by Administrators error
|
||||||
|
'100' : dedent`
|
||||||
|
## This brew has been locked.
|
||||||
|
|
||||||
|
Please contact the Administrators to unlock this document.
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
|
**Brew ID:** ${props.brew.brewId}
|
||||||
|
|
||||||
|
**Brew Title:** ${props.brew.brewTitle}`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,19 @@ const SharePage = createClass({
|
|||||||
this.props.brew.shareId;
|
this.props.brew.shareId;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderEditLink : function(){
|
||||||
|
if(!this.props.brew.editId) return;
|
||||||
|
|
||||||
|
let editLink = this.props.brew.editId;
|
||||||
|
if(this.props.brew.googleId && !this.props.brew.stubbed) {
|
||||||
|
editLink = this.props.brew.googleId + editLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Nav.item color='orange' icon='fas fa-pencil-alt' href={`/edit/${editLink}`}>
|
||||||
|
edit
|
||||||
|
</Nav.item>;
|
||||||
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <div className='sharePage sitePage'>
|
return <div className='sharePage sitePage'>
|
||||||
<Meta name='robots' content='noindex, nofollow' />
|
<Meta name='robots' content='noindex, nofollow' />
|
||||||
@@ -64,13 +77,14 @@ const SharePage = createClass({
|
|||||||
<Nav.item color='red' icon='fas fa-code'>
|
<Nav.item color='red' icon='fas fa-code'>
|
||||||
source
|
source
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item color='blue' href={`/source/${this.processShareId()}`}>
|
<Nav.item color='blue' icon='fas fa-eye' href={`/source/${this.processShareId()}`}>
|
||||||
view
|
view
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item color='blue' href={`/download/${this.processShareId()}`}>
|
{this.renderEditLink()}
|
||||||
|
<Nav.item color='blue' icon='fas fa-download' href={`/download/${this.processShareId()}`}>
|
||||||
download
|
download
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item color='blue' href={`/new/${this.processShareId()}`}>
|
<Nav.item color='blue' icon='fas fa-clone' href={`/new/${this.processShareId()}`}>
|
||||||
clone to new
|
clone to new
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
</Nav.dropdown>
|
</Nav.dropdown>
|
||||||
|
|||||||
1352
package-lock.json
generated
1352
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"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.11.0",
|
"version": "3.12.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": "^10.2.x",
|
"npm": "^10.2.x",
|
||||||
"node": "^20.8.x"
|
"node": "^20.8.x"
|
||||||
@@ -27,10 +27,12 @@
|
|||||||
"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:variables": "jest tests/markdown/variables.test.js --verbose",
|
"test:variables": "jest tests/markdown/variables.test.js --verbose",
|
||||||
"test:mustache-syntax": "jest '.*(mustache-syntax).*' --verbose --noStackTrace",
|
"test:mustache-syntax": "jest \".*(mustache-syntax).*\" --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --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:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:injection": "jest '.*(mustache-syntax).*' -t '^Injection:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace",
|
||||||
|
"test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
|
||||||
|
"test:emojis": "jest tests/markdown/emojis.test.js --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",
|
||||||
@@ -80,19 +82,19 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.24.0",
|
"@babel/core": "^7.24.5",
|
||||||
"@babel/plugin-transform-runtime": "^7.24.0",
|
"@babel/plugin-transform-runtime": "^7.24.3",
|
||||||
"@babel/preset-env": "^7.24.0",
|
"@babel/preset-env": "^7.24.5",
|
||||||
"@babel/preset-react": "^7.23.3",
|
"@babel/preset-react": "^7.24.1",
|
||||||
"@googleapis/drive": "^8.7.0",
|
"@googleapis/drive": "^8.8.0",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.5.1",
|
||||||
"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.3",
|
"dedent-tabs": "^0.10.3",
|
||||||
"expr-eval": "^2.0.2",
|
"expr-eval": "^2.0.2",
|
||||||
"express": "^4.18.3",
|
"express": "^4.19.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.2.0",
|
"fs-extra": "11.2.0",
|
||||||
@@ -101,26 +103,27 @@
|
|||||||
"less": "^3.13.1",
|
"less": "^3.13.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "11.2.0",
|
"marked": "11.2.0",
|
||||||
|
"marked-emoji": "^1.4.0",
|
||||||
"marked-extended-tables": "^1.0.8",
|
"marked-extended-tables": "^1.0.8",
|
||||||
"marked-gfm-heading-id": "^3.1.3",
|
"marked-gfm-heading-id": "^3.1.3",
|
||||||
"marked-smartypants-lite": "^1.0.2",
|
"marked-smartypants-lite": "^1.0.2",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"mongoose": "^8.2.1",
|
"mongoose": "^8.3.3",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.1",
|
"nconf": "^0.12.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"react-frame-component": "^4.1.3",
|
"react-frame-component": "^4.1.3",
|
||||||
"react-router-dom": "6.22.3",
|
"react-router-dom": "6.23.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^8.1.2",
|
"superagent": "^9.0.2",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-jest": "^27.9.0",
|
"eslint-plugin-jest": "^28.5.0",
|
||||||
"eslint-plugin-react": "^7.34.0",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
@@ -128,6 +131,6 @@
|
|||||||
"stylelint-config-recess-order": "^4.6.0",
|
"stylelint-config-recess-order": "^4.6.0",
|
||||||
"stylelint-config-recommended": "^13.0.0",
|
"stylelint-config-recommended": "^13.0.0",
|
||||||
"stylelint-stylistic": "^0.4.3",
|
"stylelint-stylistic": "^0.4.3",
|
||||||
"supertest": "^6.3.4"
|
"supertest": "^7.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"codemirror/addon/edit/closetag.js",
|
"codemirror/addon/edit/closetag.js",
|
||||||
"codemirror/addon/edit/trailingspace.js",
|
"codemirror/addon/edit/trailingspace.js",
|
||||||
"codemirror/addon/selection/active-line.js",
|
"codemirror/addon/selection/active-line.js",
|
||||||
|
"codemirror/addon/hint/show-hint.js",
|
||||||
"moment",
|
"moment",
|
||||||
"superagent"
|
"superagent"
|
||||||
]
|
]
|
||||||
|
|||||||
10
server.js
10
server.js
@@ -7,6 +7,14 @@ DB.connect(config).then(()=>{
|
|||||||
// before launching server
|
// before launching server
|
||||||
const PORT = process.env.PORT || config.get('web_port') || 8000;
|
const PORT = process.env.PORT || config.get('web_port') || 8000;
|
||||||
server.app.listen(PORT, ()=>{
|
server.app.listen(PORT, ()=>{
|
||||||
console.log(`server on port: ${PORT}`);
|
const reset = '\x1b[0m'; // Reset to default style
|
||||||
|
const bright = '\x1b[1m'; // Bright (bold) style
|
||||||
|
const cyan = '\x1b[36m'; // Cyan color
|
||||||
|
const underline = '\x1b[4m'; // Underlined style
|
||||||
|
|
||||||
|
console.log(`\n\tserver started at: ${new Date().toLocaleString()}`);
|
||||||
|
console.log(`\tserver on port: ${PORT}`);
|
||||||
|
console.log(`\t${bright + cyan}Open in browser: ${reset}${underline + bright + cyan}http://localhost:${PORT}${reset}\n\n`)
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ const { splitTextStyleAndMetadata } = require('../shared/helpers.js');
|
|||||||
const sanitizeBrew = (brew, accessType)=>{
|
const sanitizeBrew = (brew, accessType)=>{
|
||||||
brew._id = undefined;
|
brew._id = undefined;
|
||||||
brew.__v = undefined;
|
brew.__v = undefined;
|
||||||
if(accessType !== 'edit'){
|
if(accessType !== 'edit' && accessType !== 'shareAuthor') {
|
||||||
brew.editId = undefined;
|
brew.editId = undefined;
|
||||||
}
|
}
|
||||||
return brew;
|
return brew;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -308,7 +308,6 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
|
|||||||
//Share Page
|
//Share Page
|
||||||
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
|
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
|
||||||
const { brew } = req;
|
const { brew } = req;
|
||||||
|
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
title : req.brew.title || 'Untitled Brew',
|
title : req.brew.title || 'Untitled Brew',
|
||||||
description : req.brew.description || 'No description.',
|
description : req.brew.description || 'No description.',
|
||||||
@@ -327,7 +326,8 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
|
|||||||
await HomebrewModel.increaseView({ shareId: brew.shareId });
|
await HomebrewModel.increaseView({ shareId: brew.shareId });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
sanitizeBrew(req.brew, 'share');
|
|
||||||
|
brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share');
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
@@ -373,7 +373,7 @@ app.get('/account', asyncHandler(async (req, res, next)=>{
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
data.uiItems = {
|
data.accountDetails = {
|
||||||
username : req.account.username,
|
username : req.account.username,
|
||||||
issued : req.account.issued,
|
issued : req.account.issued,
|
||||||
googleId : Boolean(req.account.googleId),
|
googleId : Boolean(req.account.googleId),
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ const config = require('./config.js');
|
|||||||
|
|
||||||
let serviceAuth;
|
let serviceAuth;
|
||||||
if(!config.get('service_account')){
|
if(!config.get('service_account')){
|
||||||
console.log('No Google Service Account in config files - Google Drive integration will not be available.');
|
const reset = '\x1b[0m'; // Reset to default style
|
||||||
|
const yellow = '\x1b[33m'; // yellow color
|
||||||
|
console.warn(`\n${yellow}No Google Service Account in config files - Google Drive integration will not be available.${reset}`);
|
||||||
} else {
|
} else {
|
||||||
const keys = typeof(config.get('service_account')) == 'string' ?
|
const keys = typeof(config.get('service_account')) == 'string' ?
|
||||||
JSON.parse(config.get('service_account')) :
|
JSON.parse(config.get('service_account')) :
|
||||||
@@ -18,7 +20,7 @@ if(!config.get('service_account')){
|
|||||||
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);
|
||||||
console.log('Please make sure the Google Service Account is set up properly in your config files.');
|
console.warn('Please make sure the Google Service Account is set up properly in your config files.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ const api = {
|
|||||||
});
|
});
|
||||||
stub = stub?.toObject();
|
stub = stub?.toObject();
|
||||||
|
|
||||||
|
if(stub?.lock?.locked && accessType != 'edit') {
|
||||||
|
throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.message, brewId: stub.shareId, brewTitle: stub.title };
|
||||||
|
}
|
||||||
|
|
||||||
// If there is a google id, try to find the google brew
|
// If there is a google id, try to find the google brew
|
||||||
if(!stubOnly && (googleId || stub?.googleId)) {
|
if(!stubOnly && (googleId || stub?.googleId)) {
|
||||||
let googleError;
|
let googleError;
|
||||||
@@ -79,9 +83,9 @@ const api = {
|
|||||||
if(accessType === 'edit' && (authorsExist && !(isAuthor || isInvited))) {
|
if(accessType === 'edit' && (authorsExist && !(isAuthor || isInvited))) {
|
||||||
const accessError = { name: 'Access Error', status: 401 };
|
const accessError = { name: 'Access Error', status: 401 };
|
||||||
if(req.account){
|
if(req.account){
|
||||||
throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId };
|
throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId};
|
||||||
}
|
}
|
||||||
throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title };
|
throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If after all of that we still don't have a brew, throw an exception
|
// If after all of that we still don't have a brew, throw an exception
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ describe('Tests for api', ()=>{
|
|||||||
id : '123456789012345678901234567890123abcdefghijkl'
|
id : '123456789012345678901234567890123abcdefghijkl'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(googleId).toEqual('123456789012345678901234567890123');
|
expect(googleId).toEqual('123456789012345678901234567890123');
|
||||||
expect(id).toEqual('abcdefghijkl');
|
expect(id).toEqual('abcdefghijkl');
|
||||||
});
|
});
|
||||||
@@ -128,7 +128,7 @@ describe('Tests for api', ()=>{
|
|||||||
id : '123456789012345678901234567890123abcdefghij'
|
id : '123456789012345678901234567890123abcdefghij'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(googleId).toEqual('123456789012345678901234567890123');
|
expect(googleId).toEqual('123456789012345678901234567890123');
|
||||||
expect(id).toEqual('abcdefghij');
|
expect(id).toEqual('abcdefghij');
|
||||||
});
|
});
|
||||||
@@ -298,6 +298,18 @@ describe('Tests for api', ()=>{
|
|||||||
expect(model.get).toHaveBeenCalledWith({ shareId: '1' });
|
expect(model.get).toHaveBeenCalledWith({ shareId: '1' });
|
||||||
expect(google.getGoogleBrew).toHaveBeenCalledWith('2', '1', 'share');
|
expect(google.getGoogleBrew).toHaveBeenCalledWith('2', '1', 'share');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('access is denied to a locked brew', async()=>{
|
||||||
|
const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, message: 'brew locked' } };
|
||||||
|
model.get = jest.fn(()=>toBrewPromise(lockBrew));
|
||||||
|
api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
|
||||||
|
|
||||||
|
const fn = api.getBrew('share', false);
|
||||||
|
const req = { brew: {} };
|
||||||
|
const next = jest.fn();
|
||||||
|
|
||||||
|
await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '100', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('mergeBrewText', ()=>{
|
describe('mergeBrewText', ()=>{
|
||||||
|
|||||||
82
shared/naturalcrit/codeEditor/autocompleteEmoji.js
Normal file
82
shared/naturalcrit/codeEditor/autocompleteEmoji.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
const diceFont = require('../../../themes/fonts/iconFonts/diceFont.js');
|
||||||
|
const elderberryInn = require('../../../themes/fonts/iconFonts/elderberryInn.js');
|
||||||
|
const fontAwesome = require('../../../themes/fonts/iconFonts/fontAwesome.js');
|
||||||
|
|
||||||
|
const emojis = {
|
||||||
|
...diceFont,
|
||||||
|
...elderberryInn,
|
||||||
|
...fontAwesome
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAutocompleteEmoji = function(CodeMirror, editor) {
|
||||||
|
CodeMirror.commands.autocomplete = function(editor) {
|
||||||
|
editor.showHint({
|
||||||
|
completeSingle : false,
|
||||||
|
hint : function(editor) {
|
||||||
|
const cursor = editor.getCursor();
|
||||||
|
const line = cursor.line;
|
||||||
|
const lineContent = editor.getLine(line);
|
||||||
|
const start = lineContent.lastIndexOf(':', cursor.ch - 1) + 1;
|
||||||
|
const end = cursor.ch;
|
||||||
|
const currentWord = lineContent.slice(start, end);
|
||||||
|
|
||||||
|
|
||||||
|
const list = Object.keys(emojis).filter(function(emoji) {
|
||||||
|
return emoji.toLowerCase().indexOf(currentWord.toLowerCase()) >= 0;
|
||||||
|
}).sort((a, b)=>{
|
||||||
|
const lowerA = a.replace(/\d+/g, function(match) { // Temporarily convert any numbers in emoji string
|
||||||
|
return match.padStart(4, '0'); // to 4-digits, left-padded with 0's, to aid in
|
||||||
|
}).toLowerCase(); // sorting numbers, i.e., "d6, d10, d20", not "d10, d20, d6"
|
||||||
|
const lowerB = b.replace(/\d+/g, function(match) { // Also make lowercase for case-insensitive alpha sorting
|
||||||
|
return match.padStart(4, '0');
|
||||||
|
}).toLowerCase();
|
||||||
|
|
||||||
|
if(lowerA < lowerB)
|
||||||
|
return -1;
|
||||||
|
return 1;
|
||||||
|
}).map(function(emoji) {
|
||||||
|
return {
|
||||||
|
text : `${emoji}:`, // Text to output to editor when option is selected
|
||||||
|
render : function(element, self, data) { // How to display the option in the dropdown
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = `<i class="emojiPreview ${emojis[emoji]}"></i> ${emoji}`;
|
||||||
|
element.appendChild(div);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
list : list.length ? list : [],
|
||||||
|
from : CodeMirror.Pos(line, start),
|
||||||
|
to : CodeMirror.Pos(line, end)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.on('inputRead', function(instance, change) {
|
||||||
|
const cursor = editor.getCursor();
|
||||||
|
const line = editor.getLine(cursor.line);
|
||||||
|
|
||||||
|
// Get the text from the start of the line to the cursor
|
||||||
|
const textToCursor = line.slice(0, cursor.ch);
|
||||||
|
|
||||||
|
// Do not autosuggest emojis in curly span/div/injector properties
|
||||||
|
if(line.includes('{')) {
|
||||||
|
const curlyToCursor = textToCursor.slice(textToCursor.indexOf(`{`));
|
||||||
|
const curlySpanRegex = /{(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\1$/g;
|
||||||
|
|
||||||
|
if(curlySpanRegex.test(curlyToCursor))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the text ends with ':xyz'
|
||||||
|
if(/:[^\s:]+$/.test(textToCursor)) {
|
||||||
|
CodeMirror.commands.autocomplete(editor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
showAutocompleteEmoji
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@ const createClass = require('create-react-class');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const closeTag = require('./close-tag');
|
const closeTag = require('./close-tag');
|
||||||
|
const autoCompleteEmoji = require('./autocompleteEmoji');
|
||||||
|
|
||||||
let CodeMirror;
|
let CodeMirror;
|
||||||
if(typeof window !== 'undefined'){
|
if(typeof window !== 'undefined'){
|
||||||
@@ -36,6 +37,8 @@ if(typeof window !== 'undefined'){
|
|||||||
//XML code folding is a requirement of the auto-closing tag feature and is not enabled
|
//XML code folding is a requirement of the auto-closing tag feature and is not enabled
|
||||||
require('codemirror/addon/fold/xml-fold.js');
|
require('codemirror/addon/fold/xml-fold.js');
|
||||||
require('codemirror/addon/edit/closetag.js');
|
require('codemirror/addon/edit/closetag.js');
|
||||||
|
//Autocompletion
|
||||||
|
require('codemirror/addon/hint/show-hint.js');
|
||||||
|
|
||||||
const foldCode = require('./fold-code');
|
const foldCode = require('./fold-code');
|
||||||
foldCode.registerHomebreweryHelper(CodeMirror);
|
foldCode.registerHomebreweryHelper(CodeMirror);
|
||||||
@@ -177,7 +180,10 @@ const CodeEditor = createClass({
|
|||||||
// return el;
|
// return el;
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add custom behaviors (auto-close curlies and auto-complete emojis)
|
||||||
closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror);
|
closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror);
|
||||||
|
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
|
||||||
|
|
||||||
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
|
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
|
||||||
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
|
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
|
||||||
@@ -436,7 +442,7 @@ const CodeEditor = createClass({
|
|||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <>
|
return <>
|
||||||
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} type="text/css" rel='stylesheet' />
|
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} type='text/css' rel='stylesheet' />
|
||||||
<div className='codeEditor' ref='editor' style={this.props.style}/>
|
<div className='codeEditor' ref='editor' style={this.props.style}/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
@import (less) 'codemirror/addon/fold/foldgutter.css';
|
@import (less) 'codemirror/addon/fold/foldgutter.css';
|
||||||
@import (less) 'codemirror/addon/search/matchesonscrollbar.css';
|
@import (less) 'codemirror/addon/search/matchesonscrollbar.css';
|
||||||
@import (less) 'codemirror/addon/dialog/dialog.css';
|
@import (less) 'codemirror/addon/dialog/dialog.css';
|
||||||
|
@import (less) 'codemirror/addon/hint/show-hint.css';
|
||||||
|
|
||||||
|
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
||||||
|
@import (less) './themes/fonts/iconFonts/diceFont.less';
|
||||||
|
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
|
||||||
|
|
||||||
@keyframes sourceMoveAnimation {
|
@keyframes sourceMoveAnimation {
|
||||||
50% {background-color: red; color: white;}
|
50% {background-color: red; color: white;}
|
||||||
@@ -17,13 +22,24 @@
|
|||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sourceMoveFlash .CodeMirror-line{
|
.sourceMoveFlash .CodeMirror-line{
|
||||||
animation-name: sourceMoveAnimation;
|
animation-name: sourceMoveAnimation;
|
||||||
animation-duration: 0.4s;
|
animation-duration: 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.CodeMirror-vscrollbar {
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
width: 20px;
|
||||||
|
background: linear-gradient(90deg, #858585 15px, #808080 15px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//.cm-tab {
|
//.cm-tab {
|
||||||
// background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAQAAACOs/baAAAARUlEQVR4nGJgIAG8JkXxUAcCtDWemcGR1lY4MvgzCEKY7jSBjgxBDAG09UEQzAe0AMwMHrSOAwEGRtpaMIwAAAAA//8DAG4ID9EKs6YqAAAAAElFTkSuQmCC) no-repeat right;
|
// background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAQAAACOs/baAAAARUlEQVR4nGJgIAG8JkXxUAcCtDWemcGR1lY4MvgzCEKY7jSBjgxBDAG09UEQzAe0AMwMHrSOAwEGRtpaMIwAAAAA//8DAG4ID9EKs6YqAAAAAElFTkSuQmCC) no-repeat right;
|
||||||
//}
|
//}
|
||||||
@@ -34,3 +50,8 @@
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emojiPreview {
|
||||||
|
font-size: 1.5em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
@@ -4,6 +4,13 @@ const Marked = require('marked');
|
|||||||
const MarkedExtendedTables = require('marked-extended-tables');
|
const MarkedExtendedTables = require('marked-extended-tables');
|
||||||
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
|
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
|
||||||
const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
|
const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
|
||||||
|
const { markedEmoji: MarkedEmojis } = require('marked-emoji');
|
||||||
|
|
||||||
|
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
||||||
|
const diceFont = require('../../themes/fonts/iconFonts/diceFont.js');
|
||||||
|
const elderberryInn = require('../../themes/fonts/iconFonts/elderberryInn.js');
|
||||||
|
const fontAwesome = require('../../themes/fonts/iconFonts/fontAwesome.js');
|
||||||
|
|
||||||
const MathParser = require('expr-eval').Parser;
|
const MathParser = require('expr-eval').Parser;
|
||||||
const renderer = new Marked.Renderer();
|
const renderer = new Marked.Renderer();
|
||||||
const tokenizer = new Marked.Tokenizer();
|
const tokenizer = new Marked.Tokenizer();
|
||||||
@@ -50,7 +57,7 @@ renderer.html = function (html) {
|
|||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't wrap {{ Divs or {{ empty Spans in <p> tags
|
// Don't wrap {{ Spans alone on a line, or {{ Divs in <p> tags
|
||||||
renderer.paragraph = function(text){
|
renderer.paragraph = function(text){
|
||||||
let match;
|
let match;
|
||||||
if(text.startsWith('<div') || text.startsWith('</div'))
|
if(text.startsWith('<div') || text.startsWith('</div'))
|
||||||
@@ -99,13 +106,13 @@ const mustacheSpans = {
|
|||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
let blockCount = 0;
|
let blockCount = 0;
|
||||||
let tags = '';
|
let tags = {};
|
||||||
let endTags = 0;
|
let endTags = 0;
|
||||||
let endToken = 0;
|
let endToken = 0;
|
||||||
let delim;
|
let delim;
|
||||||
while (delim = inlineRegex.exec(match[0])) {
|
while (delim = inlineRegex.exec(match[0])) {
|
||||||
if(!tags) {
|
if(_.isEmpty(tags)) {
|
||||||
tags = `${processStyleTags(delim[0].substring(2))}`;
|
tags = processStyleTags(delim[0].substring(2));
|
||||||
endTags = delim[0].length;
|
endTags = delim[0].length;
|
||||||
}
|
}
|
||||||
if(delim[0].startsWith('{{')) {
|
if(delim[0].startsWith('{{')) {
|
||||||
@@ -134,7 +141,14 @@ const mustacheSpans = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
renderer(token) {
|
renderer(token) {
|
||||||
return `<span class="inline-block${token.tags}>${this.parser.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
|
const tags = token.tags;
|
||||||
|
tags.classes = ['inline-block', tags.classes].join(' ').trim();
|
||||||
|
return `<span` +
|
||||||
|
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
|
||||||
|
`${tags.id ? ` id="${tags.id}"` : ''}` +
|
||||||
|
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
|
||||||
|
`${tags.attributes ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
|
||||||
|
`>${this.parser.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,13 +163,13 @@ const mustacheDivs = {
|
|||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
let blockCount = 0;
|
let blockCount = 0;
|
||||||
let tags = '';
|
let tags = {};
|
||||||
let endTags = 0;
|
let endTags = 0;
|
||||||
let endToken = 0;
|
let endToken = 0;
|
||||||
let delim;
|
let delim;
|
||||||
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
||||||
if(!tags) {
|
if(_.isEmpty(tags)) {
|
||||||
tags = `${processStyleTags(delim.substring(2))}`;
|
tags = processStyleTags(delim.substring(2));
|
||||||
endTags = delim.length + src.indexOf(delim);
|
endTags = delim.length + src.indexOf(delim);
|
||||||
}
|
}
|
||||||
if(delim.startsWith('{{')) {
|
if(delim.startsWith('{{')) {
|
||||||
@@ -183,7 +197,14 @@ const mustacheDivs = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
renderer(token) {
|
renderer(token) {
|
||||||
return `<div class="block${token.tags}>${this.parser.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML
|
const tags = token.tags;
|
||||||
|
tags.classes = ['block', tags.classes].join(' ').trim();
|
||||||
|
return `<div` +
|
||||||
|
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
|
||||||
|
`${tags.id ? ` id="${tags.id}"` : ''}` +
|
||||||
|
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
|
||||||
|
`${tags.attributes ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
|
||||||
|
`>${this.parser.parse(token.tokens)}</div>`; // parse to turn child tokens into HTML
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,23 +220,39 @@ const mustacheInjectInline = {
|
|||||||
if(!lastToken || lastToken.type == 'mustacheInjectInline')
|
if(!lastToken || lastToken.type == 'mustacheInjectInline')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const tags = `${processStyleTags(match[1])}`;
|
const tags = processStyleTags(match[1]);
|
||||||
lastToken.originalType = lastToken.type;
|
lastToken.originalType = lastToken.type;
|
||||||
lastToken.type = 'mustacheInjectInline';
|
lastToken.type = 'mustacheInjectInline';
|
||||||
lastToken.tags = tags;
|
lastToken.injectedTags = tags;
|
||||||
return {
|
return {
|
||||||
type : 'text', // Should match "name" above
|
type : 'mustacheInjectInline', // Should match "name" above
|
||||||
raw : match[0], // Text to consume from the source
|
raw : match[0], // Text to consume from the source
|
||||||
text : ''
|
text : ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
renderer(token) {
|
renderer(token) {
|
||||||
|
if(!token.originalType){
|
||||||
|
return;
|
||||||
|
}
|
||||||
token.type = token.originalType;
|
token.type = token.originalType;
|
||||||
const text = this.parser.parseInline([token]);
|
const text = this.parser.parseInline([token]);
|
||||||
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
|
const originalTags = extractHTMLStyleTags(text);
|
||||||
|
const injectedTags = token.injectedTags;
|
||||||
|
const tags = {
|
||||||
|
id : injectedTags.id || originalTags.id || null,
|
||||||
|
classes : [originalTags.classes, injectedTags.classes].join(' ').trim() || null,
|
||||||
|
styles : [originalTags.styles, injectedTags.styles].join(' ').trim() || null,
|
||||||
|
attributes : Object.assign(originalTags.attributes ?? {}, injectedTags.attributes ?? {})
|
||||||
|
};
|
||||||
|
const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text);
|
||||||
if(openingTag) {
|
if(openingTag) {
|
||||||
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
|
return `${openingTag[1]}` +
|
||||||
|
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
|
||||||
|
`${tags.id ? ` id="${tags.id}"` : ''}` +
|
||||||
|
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
|
||||||
|
`${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
|
||||||
|
`${openingTag[2]}`; // parse to turn child tokens into HTML
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@@ -235,7 +272,7 @@ const mustacheInjectBlock = {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
lastToken.originalType = 'mustacheInjectBlock';
|
lastToken.originalType = 'mustacheInjectBlock';
|
||||||
lastToken.tags = `${processStyleTags(match[1])}`;
|
lastToken.injectedTags = processStyleTags(match[1]);
|
||||||
return {
|
return {
|
||||||
type : 'mustacheInjectBlock', // Should match "name" above
|
type : 'mustacheInjectBlock', // Should match "name" above
|
||||||
raw : match[0], // Text to consume from the source
|
raw : match[0], // Text to consume from the source
|
||||||
@@ -249,9 +286,22 @@ const mustacheInjectBlock = {
|
|||||||
}
|
}
|
||||||
token.type = token.originalType;
|
token.type = token.originalType;
|
||||||
const text = this.parser.parse([token]);
|
const text = this.parser.parse([token]);
|
||||||
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
|
const originalTags = extractHTMLStyleTags(text);
|
||||||
|
const injectedTags = token.injectedTags;
|
||||||
|
const tags = {
|
||||||
|
id : injectedTags.id || originalTags.id || null,
|
||||||
|
classes : [originalTags.classes, injectedTags.classes].join(' ').trim() || null,
|
||||||
|
styles : [originalTags.styles, injectedTags.styles].join(' ').trim() || null,
|
||||||
|
attributes : Object.assign(originalTags.attributes ?? {}, injectedTags.attributes ?? {})
|
||||||
|
};
|
||||||
|
const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text);
|
||||||
if(openingTag) {
|
if(openingTag) {
|
||||||
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
|
return `${openingTag[1]}` +
|
||||||
|
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
|
||||||
|
`${tags.id ? ` id="${tags.id}"` : ''}` +
|
||||||
|
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
|
||||||
|
`${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
|
||||||
|
`${openingTag[2]}`; // parse to turn child tokens into HTML
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@@ -294,25 +344,34 @@ const superSubScripts = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const definitionLists = {
|
const definitionListsSingleLine = {
|
||||||
name : 'definitionLists',
|
name : 'definitionListsSingleLine',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/^.*?::.*/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
|
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
|
||||||
let match;
|
let match;
|
||||||
let endIndex = 0;
|
let endIndex = 0;
|
||||||
const definitions = [];
|
const definitions = [];
|
||||||
while (match = regex.exec(src)) {
|
while (match = regex.exec(src)) {
|
||||||
definitions.push({
|
const originalLine = match[0]; // This line and below to handle conflict with emojis
|
||||||
dt : this.lexer.inlineTokens(match[1].trim()),
|
let firstLine = originalLine; // Remove in V4 when definitionListsInline updated to
|
||||||
dd : this.lexer.inlineTokens(match[2].trim())
|
this.lexer.inlineTokens(firstLine.trim()) // require spaces around `::`
|
||||||
});
|
.filter((t)=>t.type == 'emoji')
|
||||||
|
.map((emoji)=>firstLine = firstLine.replace(emoji.raw, 'x'.repeat(emoji.raw.length)));
|
||||||
|
|
||||||
|
const newMatch = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym.exec(firstLine);
|
||||||
|
if(newMatch) {
|
||||||
|
definitions.push({
|
||||||
|
dt : this.lexer.inlineTokens(originalLine.slice(0, newMatch[1].length).trim()),
|
||||||
|
dd : this.lexer.inlineTokens(originalLine.slice(newMatch[1].length + 2).trim())
|
||||||
|
});
|
||||||
|
} // End of emoji hack.
|
||||||
endIndex = regex.lastIndex;
|
endIndex = regex.lastIndex;
|
||||||
}
|
}
|
||||||
if(definitions.length) {
|
if(definitions.length) {
|
||||||
return {
|
return {
|
||||||
type : 'definitionLists',
|
type : 'definitionListsSingleLine',
|
||||||
raw : src.slice(0, endIndex),
|
raw : src.slice(0, endIndex),
|
||||||
definitions
|
definitions
|
||||||
};
|
};
|
||||||
@@ -326,6 +385,51 @@ const definitionLists = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const definitionListsMultiLine = {
|
||||||
|
name : 'definitionListsMultiLine',
|
||||||
|
level : 'block',
|
||||||
|
start(src) { return src.match(/\n[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
|
tokenizer(src, tokens) {
|
||||||
|
const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
|
||||||
|
let match;
|
||||||
|
let endIndex = 0;
|
||||||
|
const definitions = [];
|
||||||
|
while (match = regex.exec(src)) {
|
||||||
|
if(match[1]) {
|
||||||
|
if(this.lexer.blockTokens(match[1].trim())[0]?.type !== 'paragraph') // DT must not be another block-level token besides <p>
|
||||||
|
break;
|
||||||
|
definitions.push({
|
||||||
|
dt : this.lexer.inlineTokens(match[1].trim()),
|
||||||
|
dds : []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[2] && definitions.length) {
|
||||||
|
definitions[definitions.length - 1].dds.push(
|
||||||
|
this.lexer.inlineTokens(match[2].trim().replace(/\s/g, ' '))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
endIndex = regex.lastIndex;
|
||||||
|
}
|
||||||
|
if(definitions.length) {
|
||||||
|
return {
|
||||||
|
type : 'definitionListsMultiLine',
|
||||||
|
raw : src.slice(0, endIndex),
|
||||||
|
definitions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
let returnVal = `<dl>`;
|
||||||
|
token.definitions.forEach((def)=>{
|
||||||
|
const dds = def.dds.map((s)=>{
|
||||||
|
return `\n<dd>${this.parser.parseInline(s).trim()}</dd>`;
|
||||||
|
}).join('');
|
||||||
|
returnVal += `<dt>${this.parser.parseInline(def.dt)}</dt>${dds}\n`;
|
||||||
|
});
|
||||||
|
returnVal = returnVal.trim();
|
||||||
|
return `${returnVal}</dl>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
||||||
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
||||||
@@ -571,11 +675,29 @@ function MarkedVariables() {
|
|||||||
};
|
};
|
||||||
//^=====--------------------< Variable Handling >-------------------=====^//
|
//^=====--------------------< Variable Handling >-------------------=====^//
|
||||||
|
|
||||||
|
// Emoji options
|
||||||
|
// To add more icon fonts, need to do these things
|
||||||
|
// 1) Add the font file as .woff2 to themes/fonts/iconFonts folder
|
||||||
|
// 2) Create a .less file mapping CSS class names to the font character
|
||||||
|
// 3) Create a .js file mapping Autosuggest names to CSS class names
|
||||||
|
// 4) Import the .less file into shared/naturalcrit/codeEditor/codeEditor.less
|
||||||
|
// 5) Import the .less file into themes/V3/blank.style.less
|
||||||
|
// 6) Import the .js file to shared/naturalcrit/codeEditor/autocompleteEmoji.js and add to `emojis` object
|
||||||
|
// 7) Import the .js file here to markdown.js, and add to `emojis` object below
|
||||||
|
const MarkedEmojiOptions = {
|
||||||
|
emojis : {
|
||||||
|
...diceFont,
|
||||||
|
...elderberryInn,
|
||||||
|
...fontAwesome
|
||||||
|
},
|
||||||
|
renderer : (token)=>`<i class="${token.emoji}"></i>`
|
||||||
|
};
|
||||||
|
|
||||||
Marked.use(MarkedVariables());
|
Marked.use(MarkedVariables());
|
||||||
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] });
|
Marked.use({ extensions: [definitionListsMultiLine, definitionListsSingleLine, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
||||||
Marked.use(mustacheInjectBlock);
|
Marked.use(mustacheInjectBlock);
|
||||||
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
||||||
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
|
||||||
|
|
||||||
const nonWordAndColonTest = /[^\w:]/g;
|
const nonWordAndColonTest = /[^\w:]/g;
|
||||||
const cleanUrl = function (sanitize, base, href) {
|
const cleanUrl = function (sanitize, base, href) {
|
||||||
@@ -642,15 +764,45 @@ const processStyleTags = (string)=>{
|
|||||||
//TODO: can we simplify to just split on commas?
|
//TODO: can we simplify to just split on commas?
|
||||||
const tags = string.match(/(?:[^, ":=]+|[:=](?:"[^"]*"|))+/g);
|
const tags = string.match(/(?:[^, ":=]+|[:=](?:"[^"]*"|))+/g);
|
||||||
|
|
||||||
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0];
|
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0] || null;
|
||||||
const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('=')));
|
const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('='))).join(' ') || null;
|
||||||
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'));
|
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'))
|
||||||
const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()) : [];
|
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
|
||||||
|
.reduce((obj, attr)=>{
|
||||||
|
let [key, value] = attr.split('=');
|
||||||
|
value = value.replace(/"/g, '');
|
||||||
|
obj[key] = value;
|
||||||
|
return obj;
|
||||||
|
}, {}) || null;
|
||||||
|
const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()).join(' ') : null;
|
||||||
|
|
||||||
return `${classes?.length ? ` ${classes.join(' ')}` : ''}"` +
|
return {
|
||||||
`${id ? ` id="${id}"` : ''}` +
|
id : id,
|
||||||
`${styles?.length ? ` style="${styles.join(' ')}"` : ''}` +
|
classes : classes,
|
||||||
`${attributes?.length ? ` ${attributes.join(' ')}` : ''}`;
|
styles : styles,
|
||||||
|
attributes : _.isEmpty(attributes) ? null : attributes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractHTMLStyleTags = (htmlString)=>{
|
||||||
|
const id = htmlString.match(/id="([^"]*)"/)?.[1] || null;
|
||||||
|
const classes = htmlString.match(/class="([^"]*)"/)?.[1] || null;
|
||||||
|
const styles = htmlString.match(/style="([^"]*)"/)?.[1] || null;
|
||||||
|
const attributes = htmlString.match(/[a-zA-Z]+="[^"]*"/g)
|
||||||
|
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
|
||||||
|
.reduce((obj, attr)=>{
|
||||||
|
let [key, value] = attr.split('=');
|
||||||
|
value = value.replace(/"/g, '');
|
||||||
|
obj[key] = value;
|
||||||
|
return obj;
|
||||||
|
}, {}) || null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id : id,
|
||||||
|
classes : classes,
|
||||||
|
styles : styles,
|
||||||
|
attributes : _.isEmpty(attributes) ? null : attributes
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const globalVarsList = {};
|
const globalVarsList = {};
|
||||||
|
|||||||
91
tests/markdown/definition-lists.test.js
Normal file
91
tests/markdown/definition-lists.test.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
|
describe('Inline Definition Lists', ()=>{
|
||||||
|
test('No Term 1 Definition', function() {
|
||||||
|
const source = ':: My First Definition\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt></dt><dd>My First Definition</dd>\n</dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Definition Term', function() {
|
||||||
|
const source = 'My term :: My First Definition\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>My term</dt><dd>My First Definition</dd>\n</dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Definition Terms', function() {
|
||||||
|
const source = 'Term 1::Definition of Term 1\nTerm 2::Definition of Term 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt><dd>Definition of Term 1</dd>\n<dt>Term 2</dt><dd>Definition of Term 2</dd>\n</dl>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Multiline Definition Lists', ()=>{
|
||||||
|
test('Single Term, Single Definition', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Term, Plural Definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n::Definition 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single Definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n\nTerm 2\n::Definition 1\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Plural Definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n::Definition 2\n\nTerm 2\n::Definition 1\n::Definition 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Term, Single multi-line definition', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and\nmore and more\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more and more</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Term, Plural multi-line definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n::Definition 2\nand more\nand more\n::Definition 3\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dd>Definition 2 and more and more</dd>\n<dd>Definition 3</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single multi-line definition', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single multi-line definition, followed by an inline dl', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\n::Inline Definition (no term)';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl><dl><dt></dt><dd>Inline Definition (no term)</dd>\n</dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single multi-line definition, followed by paragraph', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\nParagraph';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl><p>Paragraph</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Block Token cannot be the Term of a multi-line definition', function() {
|
||||||
|
const source = '## Header\n::Definition 1 of a single-line DL\n::Definition 1 of another single-line DL';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 id="header">Header</h2>\n<dl><dt></dt><dd>Definition 1 of a single-line DL</dd>\n<dt></dt><dd>Definition 1 of another single-line DL</dd>\n</dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Inline DL has priority over Multiline', function() {
|
||||||
|
const source = 'Term 1 :: Inline definition 1\n:: Inline definition 2 (no DT)';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt><dd>Inline definition 1</dd>\n<dt></dt><dd>Inline definition 2 (no DT)</dd>\n</dl>');
|
||||||
|
});
|
||||||
|
});
|
||||||
58
tests/markdown/emojis.test.js
Normal file
58
tests/markdown/emojis.test.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
|
// 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, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const emoji = 'df_d12_2';
|
||||||
|
|
||||||
|
describe(`When emojis/icons are active`, ()=>{
|
||||||
|
it('when a word is between two colons (:word:), and a matching emoji exists, it is rendered as an emoji', function() {
|
||||||
|
const source = `:${emoji}:`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><i class="df d12-2"></i></p>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when a word is between two colons (:word:), and no matching emoji exists, it is not parsed', function() {
|
||||||
|
const source = `:invalid:`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>:invalid:</p>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('two valid emojis with no whitespace are prioritized over definition lists', function() {
|
||||||
|
const source = `:${emoji}::${emoji}:`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><i class="df d12-2"></i><i class="df d12-2"></i></p>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('definition lists that are not also part of an emoji can coexist with normal emojis', function() {
|
||||||
|
const source = `definition :: term ${emoji}::${emoji}:`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<dl><dt>definition</dt><dd>term df_d12_2:<i class="df d12-2"></i></dd></dl>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A valid emoji is compatible with curly injectors', function() {
|
||||||
|
const source = `:${emoji}:{color:blue,myClass}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><i class="df d12-2 myClass" style="color:blue;"></i></p>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Emojis are not parsed inside of curly span CSS blocks', function() {
|
||||||
|
const source = `{{color:${emoji} text}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<span class="inline-block" style="color:df_d12_2;">text</span>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Emojis are not parsed inside of curly div CSS blocks', function() {
|
||||||
|
const source = dedent`{{color:${emoji}
|
||||||
|
text
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:df_d12_2;"><p>text</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// another test of the editor to confirm an autocomplete menu opens
|
||||||
|
});
|
||||||
@@ -130,8 +130,8 @@ describe('Inline: When using the Inline syntax {{ }}', ()=>{
|
|||||||
describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
||||||
it('Renders a div with text only', function() {
|
it('Renders a div with text only', function() {
|
||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
text
|
text
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"><p>text</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"><p>text</p></div>`);
|
||||||
});
|
});
|
||||||
@@ -139,14 +139,14 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
|||||||
it('Renders an empty div', function() {
|
it('Renders an empty div', function() {
|
||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
|
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a single paragraph with opening and closing brackets', function() {
|
it('Renders a single paragraph with opening and closing brackets', function() {
|
||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>{{}}</p>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>{{}}</p>`);
|
||||||
});
|
});
|
||||||
@@ -154,79 +154,79 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
|||||||
it('Renders a div with a single class', function() {
|
it('Renders a div with a single class', function() {
|
||||||
const source = dedent`{{cat
|
const source = dedent`{{cat
|
||||||
|
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with a single class and text', function() {
|
it('Renders a div with a single class and text', function() {
|
||||||
const source = dedent`{{cat
|
const source = dedent`{{cat
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with two classes and text', function() {
|
it('Renders a div with two classes and text', function() {
|
||||||
const source = dedent`{{cat,dog
|
const source = dedent`{{cat,dog
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat dog"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat dog"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with a style and text', function() {
|
it('Renders a div with a style and text', function() {
|
||||||
const source = dedent`{{color:red
|
const source = dedent`{{color:red
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red;"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with a style that has a string variable, and text', function() {
|
it('Renders a div with a style that has a string variable, and text', function() {
|
||||||
const source = dedent`{{--stringVariable:"'string'"
|
const source = dedent`{{--stringVariable:"'string'"
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string';"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string';"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with a style that has a string variable, and text', function() {
|
it('Renders a div with a style that has a string variable, and text', function() {
|
||||||
const source = dedent`{{--stringVariable:"'string'"
|
const source = dedent`{{--stringVariable:"'string'"
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string';"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string';"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with a class, style and text', function() {
|
it('Renders a div with a class, style and text', function() {
|
||||||
const source = dedent`{{cat,color:red
|
const source = dedent`{{cat,color:red
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" style="color:red;"><p>Sample text.</p></div>`);
|
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() {
|
it('Renders a div with an ID, class, style and text (different order)', function() {
|
||||||
const source = dedent`{{color:red,cat,#dog
|
const source = dedent`{{color:red,cat,#dog
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
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>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" id="dog" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with a single ID', function() {
|
it('Renders a div with a single ID', function() {
|
||||||
const source = dedent`{{#cat,#dog
|
const source = dedent`{{#cat,#dog
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" id="cat"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" id="cat"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders a div with an ID, class, style and text, and a variable assignment', function() {
|
it('Renders a div with an ID, class, style and text, and a variable assignment', function() {
|
||||||
const source = dedent`{{color:red,cat,#dog,a="b and c",d="e"
|
const source = dedent`{{color:red,cat,#dog,a="b and c",d="e"
|
||||||
Sample text.
|
Sample text.
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class=\"block cat\" id=\"dog\" style=\"color:red;\" a=\"b and c\" d=\"e\"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class=\"block cat\" id=\"dog\" style=\"color:red;\" a=\"b and c\" d=\"e\"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
@@ -243,61 +243,91 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
|||||||
describe('Injection: When an injection tag follows an element', ()=>{
|
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.
|
// 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', ()=>{
|
describe('and that element is an inline-block', ()=>{
|
||||||
it.failing('Renders a span "text" with no injection', function() {
|
it('Renders a span "text" with no injection', function() {
|
||||||
const source = '{{ text}}{}';
|
const source = '{{ text}}{}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">text</span>');
|
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() {
|
it('Renders a span "text" with injected Class name', function() {
|
||||||
const source = '{{ text}}{ClassName}';
|
const source = '{{ text}}{ClassName}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block ClassName">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block ClassName">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a span "text" with injected attribute', function() {
|
it('Renders a span "text" with injected attribute', function() {
|
||||||
const source = '{{ text}}{a="b and c"}';
|
const source = '{{ text}}{a="b and c"}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span a="b and c" class="inline-block ">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" a="b and c">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a span "text" with injected style', function() {
|
it('Renders a span "text" with injected style', function() {
|
||||||
const source = '{{ text}}{color:red}';
|
const source = '{{ text}}{color:red}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red;">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red;">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a span "text" with injected style using a string variable', function() {
|
it('Renders a span "text" with injected style using a string variable', function() {
|
||||||
const source = `{{ text}}{--stringVariable:"'string'"}`;
|
const source = `{{ text}}{--stringVariable:"'string'"}`;
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<span class="inline-block" style="--stringVariable:'string';">text</span>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<span class="inline-block" style="--stringVariable:'string';">text</span>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a span "text" with two injected styles', function() {
|
it('Renders a span "text" with two injected styles', function() {
|
||||||
const source = '{{ text}}{color:red,background:blue}';
|
const source = '{{ text}}{color:red,background:blue}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red; background:blue;">text</span>');
|
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() {
|
it('Renders a span "text" with its own ID, overwritten with an injected ID', function() {
|
||||||
|
const source = '{{#oldId text}}{#newId}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="newId">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, plus a new one', function() {
|
||||||
|
const source = '{{attrA="old",attrB="old" text}}{attrA="new",attrC="new"}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" attrA="new" attrB="old" attrC="new">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, ignoring "class", "style", and "id"', function() {
|
||||||
|
const source = '{{attrA="old",attrB="old" text}}{attrA="new",attrC="new",class="new",style="new",id="new"}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" attrA="new" attrB="old" attrC="new">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own styles, appended with injected styles', function() {
|
||||||
|
const source = '{{color:blue,height:10px text}}{width:10px,color:red}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:blue; height:10px; width:10px; color:red;">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own classes, appended with injected classes', function() {
|
||||||
|
const source = '{{classA,classB text}}{classA,classC}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block classA classB classA classC">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders an emphasis element with injected Class name', function() {
|
||||||
const source = '*emphasis*{big}';
|
const source = '*emphasis*{big}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><em class="big">emphasis</em></p>');
|
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() {
|
it('Renders a code element with injected style', function() {
|
||||||
const source = '`code`{background:gray}';
|
const source = '`code`{background:gray}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><code style="background:gray;">code</code></p>');
|
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() {
|
it('Renders an image element with injected style', function() {
|
||||||
const source = '{position:absolute}';
|
const source = '{position:absolute}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
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>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><img style="position:absolute;" src="http://i.imgur.com/hMna6G0.png" alt="alt text"></p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders an element modified by only the first of two consecutive injections', function() {
|
it('Renders an element modified by only the first of two consecutive injections', function() {
|
||||||
const source = '{{ text}}{color:red}{background:blue}';
|
const source = '{{ text}}{color:red}{background:blue}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
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>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><span class="inline-block" style="color:red;">text</span>{background:blue}</p>');
|
||||||
@@ -306,61 +336,106 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
it('Renders an image with added attributes', function() {
|
it('Renders an image with added attributes', function() {
|
||||||
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
|
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img class="" style="position:absolute; bottom:20px; left:130px; width:220px;" a="b and c" d="e" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug"></p>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="position:absolute; bottom:20px; left:130px; width:220px;" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug" a="b and c" d="e"></p>`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('and that element is a block', ()=>{
|
describe('and that element is a block', ()=>{
|
||||||
it.failing('renders a div "text" with no injection', function() {
|
it('renders a div "text" with no injection', function() {
|
||||||
const source = '{{\ntext\n}}\n{}';
|
const source = '{{\ntext\n}}\n{}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block"><p>text</p></div>');
|
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() {
|
it('renders a div "text" with injected Class name', function() {
|
||||||
const source = '{{\ntext\n}}\n{ClassName}';
|
const source = '{{\ntext\n}}\n{ClassName}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block ClassName"><p>text</p></div>');
|
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() {
|
it('renders a div "text" with injected style', function() {
|
||||||
const source = '{{\ntext\n}}\n{color:red}';
|
const source = '{{\ntext\n}}\n{color:red}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" style="color:red;"><p>text</p></div>');
|
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() {
|
it('renders a div "text" with two injected styles', function() {
|
||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
text
|
text
|
||||||
}}
|
}}
|
||||||
{color:red,background:blue}`;
|
{color:red,background:blue}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
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>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red; background:blue;"><p>text</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('renders a div "text" with injected variable string', function() {
|
it('renders a div "text" with injected variable string', function() {
|
||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
text
|
text
|
||||||
}}
|
}}
|
||||||
{--stringVariable:"'string'"}`;
|
{--stringVariable:"'string'"}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string'"><p>text</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string';"><p>text</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('renders an h2 header "text" with injected class name', function() {
|
it('Renders a span "text" with its own ID, overwritten with an injected ID', function() {
|
||||||
|
const source = dedent`{{#oldId
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{#newId}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" id="newId"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, plus a new one', function() {
|
||||||
|
const source = dedent`{{attrA="old",attrB="old"
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{attrA="new",attrC="new"}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" attrA="new" attrB="old" attrC="new"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, ignoring "class", "style", and "id"', function() {
|
||||||
|
const source = dedent`{{attrA="old",attrB="old"
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{attrA="new",attrC="new",class="new",style="new",id="new"}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" attrA="new" attrB="old" attrC="new"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own styles, appended with injected styles', function() {
|
||||||
|
const source = dedent`{{color:blue,height:10px
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{width:10px,color:red}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" style="color:blue; height:10px; width:10px; color:red;"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a span "text" with its own classes, appended with injected classes', function() {
|
||||||
|
const source = dedent`{{classA,classB
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{classA,classC}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block classA classB classA classC"><p>text</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders an h2 header "text" with injected class name', function() {
|
||||||
const source = dedent`## text
|
const source = dedent`## text
|
||||||
{ClassName}`;
|
{ClassName}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName">text</h2>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName" id="text">text</h2>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('renders a table with injected class name', function() {
|
it('renders a table with injected class name', function() {
|
||||||
const source = dedent`| Experience Points | Level |
|
const source = dedent`| Experience Points | Level |
|
||||||
|:------------------|:-----:|
|
|:------------------|:-----:|
|
||||||
| 0 | 1 |
|
| 0 | 1 |
|
||||||
| 300 | 2 |
|
| 300 | 2 |
|
||||||
|
|
||||||
{ClassName}`;
|
{ClassName}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
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>`);
|
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>`);
|
||||||
});
|
});
|
||||||
@@ -376,23 +451,23 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
// expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`...`); // FIXME: expect this to be injected into <ul>? Currently injects into last <li>
|
// 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() {
|
it('renders an h2 header "text" with injected class name, and "secondInjection" as regular text on the next line.', function() {
|
||||||
const source = dedent`## text
|
const source = dedent`## text
|
||||||
{ClassName}
|
{ClassName}
|
||||||
{secondInjection}`;
|
{secondInjection}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName">text</h2><p>{secondInjection}</p>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName" id="text">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() {
|
it('renders a div nested into another div, the inner with class=innerDiv and the other class=outerDiv', function() {
|
||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
outer text
|
outer text
|
||||||
{{
|
{{
|
||||||
inner text
|
inner text
|
||||||
}}
|
}}
|
||||||
{innerDiv}
|
{innerDiv}
|
||||||
}}
|
}}
|
||||||
{outerDiv}`;
|
{outerDiv}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
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>');
|
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>');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ describe('Normal Links and Images', ()=>{
|
|||||||
const source = `{width:100px}`;
|
const source = `{width:100px}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
<p><img class="" style="width:100px;" src="url" alt="alt text"></p>`.trimReturns());
|
<p><img style="width:100px;" src="url" alt="alt text"></p>`.trimReturns());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders normal links', function() {
|
it('Renders normal links', function() {
|
||||||
|
|||||||
@@ -149,8 +149,6 @@ module.exports = {
|
|||||||

|

|
||||||
|
|
||||||
Homebrewery.Naturalcrit.com
|
Homebrewery.Naturalcrit.com
|
||||||
}}
|
}}`;
|
||||||
|
|
||||||
\page`;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
@import (less) './themes/fonts/5e/fonts.less';
|
|
||||||
@import (less) './themes/assets/assets.less';
|
@import (less) './themes/assets/assets.less';
|
||||||
@import (less) './themes/fonts/icon fonts/font-icons.less';
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
//Colors
|
//Colors
|
||||||
@@ -532,21 +530,19 @@
|
|||||||
.page:has(.frontCover) {
|
.page:has(.frontCover) {
|
||||||
columns : 1;
|
columns : 1;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
&::after { all : unset; }
|
&::after { display : none; }
|
||||||
h1 {
|
h1 {
|
||||||
margin-top : 1.2cm;
|
margin-top : 1.2cm;
|
||||||
margin-bottom : 0;
|
margin-bottom : 0;
|
||||||
font-family : 'NodestoCapsCondensed';
|
font-family : 'NodestoCapsCondensed';
|
||||||
font-size : 2.245cm;
|
font-size : 2.245cm;
|
||||||
font-weight : normal;
|
font-weight : normal;
|
||||||
line-height : 0.85em;
|
line-height : 1.9cm;
|
||||||
color : white;
|
color : white;
|
||||||
text-shadow : unset;
|
text-shadow : unset;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black)
|
-webkit-text-stroke: 0.2cm black;
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
paint-order:stroke;
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-family : 'NodestoCapsCondensed';
|
font-family : 'NodestoCapsCondensed';
|
||||||
@@ -554,10 +550,8 @@
|
|||||||
font-weight : normal;
|
font-weight : normal;
|
||||||
color : white;
|
color : white;
|
||||||
letter-spacing : 0.1cm;
|
letter-spacing : 0.1cm;
|
||||||
filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black)
|
-webkit-text-stroke: 0.14cm black;
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
paint-order:stroke;
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
|
||||||
}
|
}
|
||||||
hr {
|
hr {
|
||||||
position : relative;
|
position : relative;
|
||||||
@@ -603,10 +597,8 @@
|
|||||||
font-size : 0.496cm;
|
font-size : 0.496cm;
|
||||||
color : white;
|
color : white;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black)
|
-webkit-text-stroke: 0.1cm black;
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
paint-order:stroke;
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
|
||||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
|
||||||
}
|
}
|
||||||
.logo {
|
.logo {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
@@ -626,14 +618,14 @@
|
|||||||
.page:has(.insideCover) {
|
.page:has(.insideCover) {
|
||||||
columns : 1;
|
columns : 1;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
&::after { all : unset; }
|
&::after { display : none; }
|
||||||
h1 {
|
h1 {
|
||||||
margin-top : 1.2cm;
|
margin-top : 1.2cm;
|
||||||
margin-bottom : 0;
|
margin-bottom : 0;
|
||||||
font-family : 'NodestoCapsCondensed';
|
font-family : 'NodestoCapsCondensed';
|
||||||
font-size : 2.1cm;
|
font-size : 2.1cm;
|
||||||
font-weight : normal;
|
font-weight : normal;
|
||||||
line-height : 0.85em;
|
line-height : 1.785cm;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
@@ -672,7 +664,7 @@
|
|||||||
padding : 2.25cm 1.3cm 2cm 1.3cm;
|
padding : 2.25cm 1.3cm 2cm 1.3cm;
|
||||||
color : #FFFFFF;
|
color : #FFFFFF;
|
||||||
columns : 1;
|
columns : 1;
|
||||||
&::after { all : unset; }
|
&::after { display : none; }
|
||||||
.columnWrapper { width : 7.6cm; }
|
.columnWrapper { width : 7.6cm; }
|
||||||
.backCover {
|
.backCover {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
@@ -688,7 +680,7 @@
|
|||||||
margin-bottom : 0.3cm;
|
margin-bottom : 0.3cm;
|
||||||
font-family : 'NodestoCapsCondensed';
|
font-family : 'NodestoCapsCondensed';
|
||||||
font-size : 1.35cm;
|
font-size : 1.35cm;
|
||||||
line-height : 0.95em;
|
line-height : 1.28cm;
|
||||||
color : #ED1C24;
|
color : #ED1C24;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
}
|
}
|
||||||
@@ -714,7 +706,7 @@
|
|||||||
p {
|
p {
|
||||||
font-family : 'Overpass';
|
font-family : 'Overpass';
|
||||||
font-size : 0.332cm;
|
font-size : 0.332cm;
|
||||||
line-height : 1.5em;
|
line-height : 0.35cm;
|
||||||
}
|
}
|
||||||
hr + p {
|
hr + p {
|
||||||
margin-top : 0.6cm;
|
margin-top : 0.6cm;
|
||||||
@@ -739,10 +731,10 @@
|
|||||||
font-family : 'NodestoCapsWide';
|
font-family : 'NodestoCapsWide';
|
||||||
font-size : 0.4cm;
|
font-size : 0.4cm;
|
||||||
line-height : 1em;
|
line-height : 1em;
|
||||||
|
line-height : 1.28cm;
|
||||||
color : #FFFFFF;
|
color : #FFFFFF;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
text-indent : 0;
|
text-indent : 0;
|
||||||
letter-spacing : 0.08em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -782,7 +774,7 @@
|
|||||||
margin-left : auto;
|
margin-left : auto;
|
||||||
font-family : 'Overpass';
|
font-family : 'Overpass';
|
||||||
font-size : 0.45cm;
|
font-size : 0.45cm;
|
||||||
line-height : 1.1em;
|
line-height : 0.495cm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -326,27 +326,27 @@ module.exports = [
|
|||||||
gen : dedent`{{font-family:CodeLight Dummy Text}}`
|
gen : dedent`{{font-family:CodeLight Dummy Text}}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Scaly Sans Remake',
|
name : 'Scaly Sans',
|
||||||
icon : 'font ScalySansRemake',
|
icon : 'font ScalySansRemake',
|
||||||
gen : dedent`{{font-family:ScalySansRemake Dummy Text}}`
|
gen : dedent`{{font-family:ScalySansRemake Dummy Text}}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Book Insanity Remake',
|
name : 'Book Insanity',
|
||||||
icon : 'font BookInsanityRemake',
|
icon : 'font BookInsanityRemake',
|
||||||
gen : dedent`{{font-family:BookInsanityRemake Dummy Text}}`
|
gen : dedent`{{font-family:BookInsanityRemake Dummy Text}}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Mr Eaves Remake',
|
name : 'Mr Eaves',
|
||||||
icon : 'font MrEavesRemake',
|
icon : 'font MrEavesRemake',
|
||||||
gen : dedent`{{font-family:MrEavesRemake Dummy Text}}`
|
gen : dedent`{{font-family:MrEavesRemake Dummy Text}}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Solbera Imitation Remake',
|
name: 'Solbera Imitation',
|
||||||
icon: 'font SolberaImitationRemake',
|
icon: 'font SolberaImitationRemake',
|
||||||
gen: dedent`{{font-family:SolberaImitationRemake Dummy Text}}`
|
gen: dedent`{{font-family:SolberaImitationRemake Dummy Text}}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Scaly Sans Small Caps Remake',
|
name: 'Scaly Sans Small Caps',
|
||||||
icon: 'font ScalySansSmallCapsRemake',
|
icon: 'font ScalySansSmallCapsRemake',
|
||||||
gen: dedent`{{font-family:ScalySansSmallCapsRemake Dummy Text}}`
|
gen: dedent`{{font-family:ScalySansSmallCapsRemake Dummy Text}}`
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
@import (less) './themes/fonts/5e/fonts.less';
|
@import (less) './themes/fonts/5e/fonts.less';
|
||||||
@import (less) './themes/assets/assets.less';
|
@import (less) './themes/assets/assets.less';
|
||||||
|
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
|
||||||
|
@import (less) './themes/fonts/iconFonts/diceFont.less';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
//Colors
|
//Colors
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
Icon Font: dicefont
|
|
||||||
*/
|
|
||||||
@font-face {
|
|
||||||
font-family: 'DiceFont';
|
|
||||||
src: url('../../../fonts/5e/dicefont.woff2') format('woff2'),
|
|
||||||
url('../../../fonts/5e/dicefont.woff') format('woff');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.df {
|
|
||||||
display: inline-block;
|
|
||||||
font-family: 'DiceFont';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
font-variant: normal;
|
|
||||||
line-height: 1;
|
|
||||||
text-decoration: inherit;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
text-transform: none;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-smooth: antialiased;
|
|
||||||
&.F:before { content: '\f190'; }
|
|
||||||
&.F-minus:before { content: '\f191'; }
|
|
||||||
&.F-plus:before { content: '\f192'; }
|
|
||||||
&.F-zero:before { content: '\f193'; }
|
|
||||||
&.d10:before { content: '\f194'; }
|
|
||||||
&.d10-0:before { content: '\f100'; }
|
|
||||||
&.d10-1:before { content: '\f101'; }
|
|
||||||
&.d10-10:before { content: '\f102'; }
|
|
||||||
&.d10-2:before { content: '\f103'; }
|
|
||||||
&.d10-3:before { content: '\f104'; }
|
|
||||||
&.d10-4:before { content: '\f105'; }
|
|
||||||
&.d10-5:before { content: '\f106'; }
|
|
||||||
&.d10-6:before { content: '\f107'; }
|
|
||||||
&.d10-7:before { content: '\f108'; }
|
|
||||||
&.d10-8:before { content: '\f109'; }
|
|
||||||
&.d10-9:before { content: '\f10a'; }
|
|
||||||
&.d12:before { content: '\f195'; }
|
|
||||||
&.d12-1:before { content: '\f10b'; }
|
|
||||||
&.d12-10:before { content: '\f10c'; }
|
|
||||||
&.d12-11:before { content: '\f10d'; }
|
|
||||||
&.d12-12:before { content: '\f10e'; }
|
|
||||||
&.d12-2:before { content: '\f10f'; }
|
|
||||||
&.d12-3:before { content: '\f110'; }
|
|
||||||
&.d12-4:before { content: '\f111'; }
|
|
||||||
&.d12-5:before { content: '\f112'; }
|
|
||||||
&.d12-6:before { content: '\f113'; }
|
|
||||||
&.d12-7:before { content: '\f114'; }
|
|
||||||
&.d12-8:before { content: '\f115'; }
|
|
||||||
&.d12-9:before { content: '\f116'; }
|
|
||||||
&.d2:before { content: '\f196'; }
|
|
||||||
&.d2-1:before { content: '\f117'; }
|
|
||||||
&.d2-2:before { content: '\f118'; }
|
|
||||||
&.d20:before { content: '\f197'; }
|
|
||||||
&.d20-1:before { content: '\f119'; }
|
|
||||||
&.d20-10:before { content: '\f11a'; }
|
|
||||||
&.d20-11:before { content: '\f11b'; }
|
|
||||||
&.d20-12:before { content: '\f11c'; }
|
|
||||||
&.d20-13:before { content: '\f11d'; }
|
|
||||||
&.d20-14:before { content: '\f11e'; }
|
|
||||||
&.d20-15:before { content: '\f11f'; }
|
|
||||||
&.d20-16:before { content: '\f120'; }
|
|
||||||
&.d20-17:before { content: '\f121'; }
|
|
||||||
&.d20-18:before { content: '\f122'; }
|
|
||||||
&.d20-19:before { content: '\f123'; }
|
|
||||||
&.d20-2:before { content: '\f124'; }
|
|
||||||
&.d20-20:before { content: '\f125'; }
|
|
||||||
&.d20-3:before { content: '\f126'; }
|
|
||||||
&.d20-4:before { content: '\f127'; }
|
|
||||||
&.d20-5:before { content: '\f128'; }
|
|
||||||
&.d20-6:before { content: '\f129'; }
|
|
||||||
&.d20-7:before { content: '\f12a'; }
|
|
||||||
&.d20-8:before { content: '\f12b'; }
|
|
||||||
&.d20-9:before { content: '\f12c'; }
|
|
||||||
&.d4:before { content: '\f198'; }
|
|
||||||
&.d4-1:before { content: '\f12d'; }
|
|
||||||
&.d4-2:before { content: '\f12e'; }
|
|
||||||
&.d4-3:before { content: '\f12f'; }
|
|
||||||
&.d4-4:before { content: '\f130'; }
|
|
||||||
&.d6:before { content: '\f199'; }
|
|
||||||
&.d6-1:before { content: '\f131'; }
|
|
||||||
&.d6-2:before { content: '\f132'; }
|
|
||||||
&.d6-3:before { content: '\f133'; }
|
|
||||||
&.d6-4:before { content: '\f134'; }
|
|
||||||
&.d6-5:before { content: '\f135'; }
|
|
||||||
&.d6-6:before { content: '\f136'; }
|
|
||||||
&.d8:before { content: '\f19a'; }
|
|
||||||
&.d8-1:before { content: '\f137'; }
|
|
||||||
&.d8-2:before { content: '\f138'; }
|
|
||||||
&.d8-3:before { content: '\f139'; }
|
|
||||||
&.d8-4:before { content: '\f13a'; }
|
|
||||||
&.d8-5:before { content: '\f13b'; }
|
|
||||||
&.d8-6:before { content: '\f13c'; }
|
|
||||||
&.d8-7:before { content: '\f13d'; }
|
|
||||||
&.d8-8:before { content: '\f13e'; }
|
|
||||||
&.dot-d6:before { content: '\f19b'; }
|
|
||||||
&.dot-d6-1:before { content: '\f13f'; }
|
|
||||||
&.dot-d6-2:before { content: '\f140'; }
|
|
||||||
&.dot-d6-3:before { content: '\f141'; }
|
|
||||||
&.dot-d6-4:before { content: '\f142'; }
|
|
||||||
&.dot-d6-5:before { content: '\f143'; }
|
|
||||||
&.dot-d6-6:before { content: '\f18f'; }
|
|
||||||
&.small-dot-d6-1:before { content: '\f183'; }
|
|
||||||
&.small-dot-d6-2:before { content: '\f184'; }
|
|
||||||
&.small-dot-d6-3:before { content: '\f185'; }
|
|
||||||
&.small-dot-d6-4:before { content: '\f186'; }
|
|
||||||
&.small-dot-d6-5:before { content: '\f187'; }
|
|
||||||
&.small-dot-d6-6:before { content: '\f188'; }
|
|
||||||
&.solid-small-dot-d6-1:before { content: '\f189'; }
|
|
||||||
&.solid-small-dot-d6-2:before { content: '\f18a'; }
|
|
||||||
&.solid-small-dot-d6-3:before { content: '\f18b'; }
|
|
||||||
&.solid-small-dot-d6-4:before { content: '\f18c'; }
|
|
||||||
&.solid-small-dot-d6-5:before { content: '\f18d'; }
|
|
||||||
&.solid-small-dot-d6-6:before { content: '\f18e'; }
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -1,5 +1,3 @@
|
|||||||
@import url('./dicefont.less');
|
|
||||||
|
|
||||||
/* Main Font, serif */
|
/* Main Font, serif */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: BookInsanityRemake;
|
font-family: BookInsanityRemake;
|
||||||
|
|||||||
@@ -1,224 +0,0 @@
|
|||||||
/* Main Font, serif */
|
|
||||||
@font-face {
|
|
||||||
font-family : 'Eldeberry-Inn';
|
|
||||||
font-style : normal;
|
|
||||||
font-weight : normal;
|
|
||||||
src : url('../../../fonts/icon fonts/Elderberry-Inn-Icons.woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
span.ei {
|
|
||||||
display : inline-block;
|
|
||||||
margin-right : 3px;
|
|
||||||
font-family : 'Eldeberry-Inn';
|
|
||||||
line-height : 1;
|
|
||||||
vertical-align : baseline;
|
|
||||||
-moz-osx-font-smoothing : grayscale;
|
|
||||||
-webkit-font-smoothing : antialiased;
|
|
||||||
text-rendering : auto;
|
|
||||||
|
|
||||||
&.book::before { content : '\E900'; }
|
|
||||||
&.screen::before { content : '\E901'; }
|
|
||||||
|
|
||||||
/* Spell levels */
|
|
||||||
&.spell-0::before { content : '\E902'; }
|
|
||||||
&.spell-1::before { content : '\E903'; }
|
|
||||||
&.spell-2::before { content : '\E904'; }
|
|
||||||
&.spell-3::before { content : '\E905'; }
|
|
||||||
&.spell-4::before { content : '\E906'; }
|
|
||||||
&.spell-5::before { content : '\E907'; }
|
|
||||||
&.spell-6::before { content : '\E908'; }
|
|
||||||
&.spell-7::before { content : '\E909'; }
|
|
||||||
&.spell-8::before { content : '\E90A'; }
|
|
||||||
&.spell-9::before { content : '\E90B'; }
|
|
||||||
|
|
||||||
/* Damage types */
|
|
||||||
&.acid::before { content : '\E90C'; }
|
|
||||||
&.bludgeoning::before { content : '\E90D'; }
|
|
||||||
&.cold::before { content : '\E90E'; }
|
|
||||||
&.fire::before { content : '\E90F'; }
|
|
||||||
&.force::before { content : '\E910'; }
|
|
||||||
&.lightning::before { content : '\E911'; }
|
|
||||||
&.necrotic::before { content : '\E912'; }
|
|
||||||
&.piercing::before { content : '\E914'; }
|
|
||||||
&.poison::before { content : '\E913'; }
|
|
||||||
&.psychic::before { content : '\E915'; }
|
|
||||||
&.radiant::before { content : '\E916'; }
|
|
||||||
&.slashing::before { content : '\E917'; }
|
|
||||||
&.thunder::before { content : '\E918'; }
|
|
||||||
|
|
||||||
/* DnD Conditions */
|
|
||||||
&.blinded::before { content : '\E919'; }
|
|
||||||
&.charmed::before { content : '\E91A'; }
|
|
||||||
&.deafened::before { content : '\E91B'; }
|
|
||||||
&.exhaust-1::before { content : '\E91C'; }
|
|
||||||
&.exhaust-2::before { content : '\E91D'; }
|
|
||||||
&.exhaust-3::before { content : '\E91E'; }
|
|
||||||
&.exhaust-4::before { content : '\E91F'; }
|
|
||||||
&.exhaust-5::before { content : '\E920'; }
|
|
||||||
&.exhaust-6::before { content : '\E921'; }
|
|
||||||
&.frightened::before { content : '\E922'; }
|
|
||||||
&.grappled::before { content : '\E923'; }
|
|
||||||
&.incapacitated::before { content : '\E924'; }
|
|
||||||
&.invisible::before { content : '\E926'; }
|
|
||||||
&.paralyzed::before { content : '\E927'; }
|
|
||||||
&.petrified::before { content : '\E928'; }
|
|
||||||
&.poisoned::before { content : '\E929'; }
|
|
||||||
&.prone::before { content : '\E92A'; }
|
|
||||||
&.restrained::before { content : '\E92B'; }
|
|
||||||
&.stunned::before { content : '\E92C'; }
|
|
||||||
&.unconscious::before { content : '\E925'; }
|
|
||||||
|
|
||||||
/* Character Classes and Features */
|
|
||||||
&.barbarian-rage::before { content : '\E92D'; }
|
|
||||||
&.barbarian-reckless-attack::before { content : '\E92E'; }
|
|
||||||
&.bardic-inspiration::before { content : '\E92F'; }
|
|
||||||
&.cleric-channel-divinity::before { content : '\E930'; }
|
|
||||||
&.druid-wild-shape::before { content : '\E931'; }
|
|
||||||
&.fighter-action-surge::before { content : '\E932'; }
|
|
||||||
&.fighter-second-wind::before { content : '\E933'; }
|
|
||||||
&.monk-flurry-blows::before { content : '\E934'; }
|
|
||||||
&.monk-patient-defense::before { content : '\E935'; }
|
|
||||||
&.monk-step-of-the-wind::before { content : '\E936'; }
|
|
||||||
&.monk-step-of-the-wind-2::before { content : '\E937'; }
|
|
||||||
&.monk-step-of-the-wind-3::before { content : '\E938'; }
|
|
||||||
&.monk-stunning-strike::before { content : '\E939'; }
|
|
||||||
&.monk-stunning-strike-2::before { content : '\E939'; }
|
|
||||||
&.paladin-divine-smite::before { content : '\E93B'; }
|
|
||||||
&.paladin-lay-on-hands::before { content : '\E93C'; }
|
|
||||||
&.barbarian-abilities::before { content : '\E93D'; }
|
|
||||||
&.barbarian::before { content : '\E93E'; }
|
|
||||||
&.bard-abilities::before { content : '\E93F'; }
|
|
||||||
&.bard::before { content : '\E940'; }
|
|
||||||
&.cleric-abilities::before { content : '\E941'; }
|
|
||||||
&.cleric::before { content : '\E942'; }
|
|
||||||
&.druid-abilities::before { content : '\E943'; }
|
|
||||||
&.druid::before { content : '\E944'; }
|
|
||||||
&.fighter-abilities::before { content : '\E945'; }
|
|
||||||
&.fighter::before { content : '\E946'; }
|
|
||||||
&.monk-abilities::before { content : '\E947'; }
|
|
||||||
&.monk::before { content : '\E948'; }
|
|
||||||
&.paladin-abilities::before { content : '\E949'; }
|
|
||||||
&.paladin::before { content : '\E94A'; }
|
|
||||||
&.ranger-abilities::before { content : '\E94B'; }
|
|
||||||
&.ranger::before { content : '\E94C'; }
|
|
||||||
&.rogue-abilities::before { content : '\E94D'; }
|
|
||||||
&.rogue::before { content : '\E94E'; }
|
|
||||||
&.sorcerer-abilities::before { content : '\E94F'; }
|
|
||||||
&.sorcerer::before { content : '\E950'; }
|
|
||||||
&.warlock-abilities::before { content : '\E951'; }
|
|
||||||
&.warlock::before { content : '\E952'; }
|
|
||||||
&.wizard-abilities::before { content : '\E953'; }
|
|
||||||
&.wizard::before { content : '\E954'; }
|
|
||||||
|
|
||||||
/* Types of actions */
|
|
||||||
&.movement::before { content : '\E955'; }
|
|
||||||
&.action::before { content : '\E956'; }
|
|
||||||
&.bonus-action::before { content : '\E957'; }
|
|
||||||
&.reaction::before { content : '\E958'; }
|
|
||||||
|
|
||||||
/* SRD Spells */
|
|
||||||
&.acid-arrow::before { content : '\E959'; }
|
|
||||||
&.action-1::before { content : '\E95A'; }
|
|
||||||
&.alter-self::before { content : '\E95B'; }
|
|
||||||
&.alter-self-2::before { content : '\E95C'; }
|
|
||||||
&.animal-friendship::before { content : '\E95E'; }
|
|
||||||
&.animate-dead::before { content : '\E95F'; }
|
|
||||||
&.animate-objects::before { content : '\E960'; }
|
|
||||||
&.animate-objects-2::before { content : '\E961'; }
|
|
||||||
&.bane::before { content : '\E962'; }
|
|
||||||
&.bless::before { content : '\E963'; }
|
|
||||||
&.blur::before { content : '\E964'; }
|
|
||||||
&.bonus::before { content : '\E965'; }
|
|
||||||
&.branding-smite::before { content : '\E966'; }
|
|
||||||
&.burning-hands::before { content : '\E967'; }
|
|
||||||
&.charm-person::before { content : '\E968'; }
|
|
||||||
&.chill-touch::before { content : '\E969'; }
|
|
||||||
&.cloudkill::before { content : '\E96A'; }
|
|
||||||
&.comprehend-languages::before { content : '\E96B'; }
|
|
||||||
&.cone-of-cold::before { content : '\E96C'; }
|
|
||||||
&.conjure-elemental::before { content : '\E96D'; }
|
|
||||||
&.conjure-minor-elemental::before { content : '\E96E'; }
|
|
||||||
&.control-water::before { content : '\E96F'; }
|
|
||||||
&.counterspell::before { content : '\E970'; }
|
|
||||||
&.cure-wounds::before { content : '\E971'; }
|
|
||||||
&.dancing-lights::before { content : '\E972'; }
|
|
||||||
&.darkness::before { content : '\E973'; }
|
|
||||||
&.detect-magic::before { content : '\E974'; }
|
|
||||||
&.disguise-self::before { content : '\E975'; }
|
|
||||||
&.disintegrate::before { content : '\E976'; }
|
|
||||||
&.dispel-evil-and-good::before { content : '\E977'; }
|
|
||||||
&.dispel-magic::before { content : '\E978'; }
|
|
||||||
&.dominate-monster::before { content : '\E979'; }
|
|
||||||
&.dominate-person::before { content : '\E97A'; }
|
|
||||||
&.eldritch-blast::before { content : '\E97B'; }
|
|
||||||
&.enlarge-reduce::before { content : '\E97C'; }
|
|
||||||
&.entangle::before { content : '\E97D'; }
|
|
||||||
&.faerie-fire::before { content : '\E97E'; }
|
|
||||||
&.faerie-fire2::before { content : '\E97F'; }
|
|
||||||
&.feather-fall::before { content : '\E980'; }
|
|
||||||
&.find-familiar::before { content : '\E981'; }
|
|
||||||
&.finger-of-death::before { content : '\E982'; }
|
|
||||||
&.fireball::before { content : '\E983'; }
|
|
||||||
&.floating-disk::before { content : '\E984'; }
|
|
||||||
&.fly::before { content : '\E985'; }
|
|
||||||
&.fog-cloud::before { content : '\E986'; }
|
|
||||||
&.gaseous-form::before { content : '\E987'; }
|
|
||||||
&.gaseous-form2::before { content : '\E988'; }
|
|
||||||
&.gentle-repose::before { content : '\E989'; }
|
|
||||||
&.gentle-repose2::before { content : '\E98A'; }
|
|
||||||
&.globe-of-invulnerability::before { content : '\E98B'; }
|
|
||||||
&.guiding-bolt::before { content : '\E98C'; }
|
|
||||||
&.healing-word::before { content : '\E98D'; }
|
|
||||||
&.heat-metal::before { content : '\E98E'; }
|
|
||||||
&.hellish-rebuke::before { content : '\E98F'; }
|
|
||||||
&.heroes-feast::before { content : '\E990'; }
|
|
||||||
&.heroism::before { content : '\E991'; }
|
|
||||||
&.hideous-laughter::before { content : '\E992'; }
|
|
||||||
&.identify::before { content : '\E993'; }
|
|
||||||
&.illusory-script::before { content : '\E994'; }
|
|
||||||
&.inflict-wounds::before { content : '\E995'; }
|
|
||||||
&.light::before { content : '\E996'; }
|
|
||||||
&.longstrider::before { content : '\E997'; }
|
|
||||||
&.mage-armor::before { content : '\E998'; }
|
|
||||||
&.mage-hand::before { content : '\E999'; }
|
|
||||||
&.magic-missile::before { content : '\E99A'; }
|
|
||||||
&.mass-cure-wounds::before { content : '\E99B'; }
|
|
||||||
&.mass-healing-word::before { content : '\E99C'; }
|
|
||||||
&.Mending::before { content : '\E99D'; }
|
|
||||||
&.message::before { content : '\E99E'; }
|
|
||||||
&.Minor-illusion::before { content : '\E99F'; }
|
|
||||||
&.movement1::before { content : '\E9A0'; }
|
|
||||||
&.polymorph::before { content : '\E9A1'; }
|
|
||||||
&.power-word-kill::before { content : '\E9A2'; }
|
|
||||||
&.power-word-stun::before { content : '\E9A3'; }
|
|
||||||
&.prayer-of-healing::before { content : '\E9A4'; }
|
|
||||||
&.prestidigitation::before { content : '\E9A5'; }
|
|
||||||
&.protection-from-evil-and-good::before { content : '\E9A6'; }
|
|
||||||
&.raise-read::before { content : '\E9A7'; }
|
|
||||||
&.raise-read2::before { content : '\E9A8'; }
|
|
||||||
&.reaction1::before { content : '\E9A9'; }
|
|
||||||
&.resurrection::before { content : '\E9AA'; }
|
|
||||||
&.resurrection2::before { content : '\E9AB'; }
|
|
||||||
&.revivify::before { content : '\E9AC'; }
|
|
||||||
&.revivify2::before { content : '\E9AD'; }
|
|
||||||
&.sacred-flame::before { content : '\E9AE'; }
|
|
||||||
&.sanctuary::before { content : '\E9AF'; }
|
|
||||||
&.scorching-ray::before { content : '\E9B0'; }
|
|
||||||
&.sending::before { content : '\E9B1'; }
|
|
||||||
&.shatter::before { content : '\E9B2'; }
|
|
||||||
&.shield::before { content : '\E9B3'; }
|
|
||||||
&.silent-image::before { content : '\E9B4'; }
|
|
||||||
&.sleep::before { content : '\E9B5'; }
|
|
||||||
&.speak-with-animals::before { content : '\E9B6'; }
|
|
||||||
&.telekinesis::before { content : '\E9B7'; }
|
|
||||||
&.true-strike::before { content : '\E9B8'; }
|
|
||||||
&.vicious-mockery::before { content : '\E9B9'; }
|
|
||||||
&.wall-of-fire::before { content : '\E9BA'; }
|
|
||||||
&.wall-of-force::before { content : '\E9BB'; }
|
|
||||||
&.wall-of-ice::before { content : '\E9BC'; }
|
|
||||||
&.wall-of-stone::before { content : '\E9BD'; }
|
|
||||||
&.wall-of-thorns::before { content : '\E9BE'; }
|
|
||||||
&.wish::before { content : '\E9BF'; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
96
themes/fonts/iconFonts/diceFont.js
Normal file
96
themes/fonts/iconFonts/diceFont.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const diceFont = {
|
||||||
|
'df_f' : 'df F',
|
||||||
|
'df_f_minus' : 'df F-minus',
|
||||||
|
'df_f_plus' : 'df F-plus',
|
||||||
|
'df_f_zero' : 'df F-zero',
|
||||||
|
'df_d10' : 'df d10',
|
||||||
|
'df_d10_1' : 'df d10-1',
|
||||||
|
'df_d10_10' : 'df d10-10',
|
||||||
|
'df_d10_2' : 'df d10-2',
|
||||||
|
'df_d10_3' : 'df d10-3',
|
||||||
|
'df_d10_4' : 'df d10-4',
|
||||||
|
'df_d10_5' : 'df d10-5',
|
||||||
|
'df_d10_6' : 'df d10-6',
|
||||||
|
'df_d10_7' : 'df d10-7',
|
||||||
|
'df_d10_8' : 'df d10-8',
|
||||||
|
'df_d10_9' : 'df d10-9',
|
||||||
|
'df_d12' : 'df d12',
|
||||||
|
'df_d12_1' : 'df d12-1',
|
||||||
|
'df_d12_10' : 'df d12-10',
|
||||||
|
'df_d12_11' : 'df d12-11',
|
||||||
|
'df_d12_12' : 'df d12-12',
|
||||||
|
'df_d12_2' : 'df d12-2',
|
||||||
|
'df_d12_3' : 'df d12-3',
|
||||||
|
'df_d12_4' : 'df d12-4',
|
||||||
|
'df_d12_5' : 'df d12-5',
|
||||||
|
'df_d12_6' : 'df d12-6',
|
||||||
|
'df_d12_7' : 'df d12-7',
|
||||||
|
'df_d12_8' : 'df d12-8',
|
||||||
|
'df_d12_9' : 'df d12-9',
|
||||||
|
'df_d2' : 'df d2',
|
||||||
|
'df_d2_1' : 'df d2-1',
|
||||||
|
'df_d2_2' : 'df d2-2',
|
||||||
|
'df_d20' : 'df d20',
|
||||||
|
'df_d20_1' : 'df d20-1',
|
||||||
|
'df_d20_10' : 'df d20-10',
|
||||||
|
'df_d20_11' : 'df d20-11',
|
||||||
|
'df_d20_12' : 'df d20-12',
|
||||||
|
'df_d20_13' : 'df d20-13',
|
||||||
|
'df_d20_14' : 'df d20-14',
|
||||||
|
'df_d20_15' : 'df d20-15',
|
||||||
|
'df_d20_16' : 'df d20-16',
|
||||||
|
'df_d20_17' : 'df d20-17',
|
||||||
|
'df_d20_18' : 'df d20-18',
|
||||||
|
'df_d20_19' : 'df d20-19',
|
||||||
|
'df_d20_2' : 'df d20-2',
|
||||||
|
'df_d20_20' : 'df d20-20',
|
||||||
|
'df_d20_3' : 'df d20-3',
|
||||||
|
'df_d20_4' : 'df d20-4',
|
||||||
|
'df_d20_5' : 'df d20-5',
|
||||||
|
'df_d20_6' : 'df d20-6',
|
||||||
|
'df_d20_7' : 'df d20-7',
|
||||||
|
'df_d20_8' : 'df d20-8',
|
||||||
|
'df_d20_9' : 'df d20-9',
|
||||||
|
'df_d4' : 'df d4',
|
||||||
|
'df_d4_1' : 'df d4-1',
|
||||||
|
'df_d4_2' : 'df d4-2',
|
||||||
|
'df_d4_3' : 'df d4-3',
|
||||||
|
'df_d4_4' : 'df d4-4',
|
||||||
|
'df_d6' : 'df d6',
|
||||||
|
'df_d6_1' : 'df d6-1',
|
||||||
|
'df_d6_2' : 'df d6-2',
|
||||||
|
'df_d6_3' : 'df d6-3',
|
||||||
|
'df_d6_4' : 'df d6-4',
|
||||||
|
'df_d6_5' : 'df d6-5',
|
||||||
|
'df_d6_6' : 'df d6-6',
|
||||||
|
'df_d8' : 'df d8',
|
||||||
|
'df_d8_1' : 'df d8-1',
|
||||||
|
'df_d8_2' : 'df d8-2',
|
||||||
|
'df_d8_3' : 'df d8-3',
|
||||||
|
'df_d8_4' : 'df d8-4',
|
||||||
|
'df_d8_5' : 'df d8-5',
|
||||||
|
'df_d8_6' : 'df d8-6',
|
||||||
|
'df_d8_7' : 'df d8-7',
|
||||||
|
'df_d8_8' : 'df d8-8',
|
||||||
|
'df_dot_d6' : 'df dot-d6',
|
||||||
|
'df_dot_d6_1' : 'df dot-d6-1',
|
||||||
|
'df_dot_d6_2' : 'df dot-d6-2',
|
||||||
|
'df_dot_d6_3' : 'df dot-d6-3',
|
||||||
|
'df_dot_d6_4' : 'df dot-d6-4',
|
||||||
|
'df_dot_d6_5' : 'df dot-d6-5',
|
||||||
|
'df_dot_d6_6' : 'df dot-d6-6',
|
||||||
|
'df_small_dot_d6_1' : 'df small-dot-d6-1',
|
||||||
|
'df_small_dot_d6_2' : 'df small-dot-d6-2',
|
||||||
|
'df_small_dot_d6_3' : 'df small-dot-d6-3',
|
||||||
|
'df_small_dot_d6_4' : 'df small-dot-d6-4',
|
||||||
|
'df_small_dot_d6_5' : 'df small-dot-d6-5',
|
||||||
|
'df_small_dot_d6_6' : 'df small-dot-d6-6',
|
||||||
|
'df_solid_small_dot_d6_1' : 'df solid-small-dot-d6-1',
|
||||||
|
'df_solid_small_dot_d6_2' : 'df solid-small-dot-d6-2',
|
||||||
|
'df_solid_small_dot_d6_3' : 'df solid-small-dot-d6-3',
|
||||||
|
'df_solid_small_dot_d6_4' : 'df solid-small-dot-d6-4',
|
||||||
|
'df_solid_small_dot_d6_5' : 'df solid-small-dot-d6-5',
|
||||||
|
'df_solid_small_dot_d6_6' : 'df solid-small-dot-d6-6'
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = diceFont;
|
||||||
114
themes/fonts/iconFonts/diceFont.less
Normal file
114
themes/fonts/iconFonts/diceFont.less
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/* Icon Font: diceFont */
|
||||||
|
@font-face {
|
||||||
|
font-family : 'DiceFont';
|
||||||
|
font-style : normal;
|
||||||
|
font-weight : normal;
|
||||||
|
src : url('../../../fonts/iconFonts/diceFont.woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
.df {
|
||||||
|
display : inline-block;
|
||||||
|
font-family : 'DiceFont';
|
||||||
|
font-style : normal;
|
||||||
|
font-weight : normal;
|
||||||
|
font-variant : normal;
|
||||||
|
line-height : 1;
|
||||||
|
text-decoration : inherit;
|
||||||
|
text-transform : none;
|
||||||
|
text-rendering : optimizeLegibility;
|
||||||
|
-moz-osx-font-smoothing : grayscale;
|
||||||
|
-webkit-font-smoothing : antialiased;
|
||||||
|
&.F::before { content : '\f190'; }
|
||||||
|
&.F-minus::before { content : '\f191'; }
|
||||||
|
&.F-plus::before { content : '\f192'; }
|
||||||
|
&.F-zero::before { content : '\f193'; }
|
||||||
|
&.d10::before { content : '\f194'; }
|
||||||
|
&.d10-0::before { content : '\f100'; }
|
||||||
|
&.d10-1::before { content : '\f101'; }
|
||||||
|
&.d10-10::before { content : '\f102'; }
|
||||||
|
&.d10-2::before { content : '\f103'; }
|
||||||
|
&.d10-3::before { content : '\f104'; }
|
||||||
|
&.d10-4::before { content : '\f105'; }
|
||||||
|
&.d10-5::before { content : '\f106'; }
|
||||||
|
&.d10-6::before { content : '\f107'; }
|
||||||
|
&.d10-7::before { content : '\f108'; }
|
||||||
|
&.d10-8::before { content : '\f109'; }
|
||||||
|
&.d10-9::before { content : '\f10a'; }
|
||||||
|
&.d12::before { content : '\f195'; }
|
||||||
|
&.d12-1::before { content : '\f10b'; }
|
||||||
|
&.d12-10::before { content : '\f10c'; }
|
||||||
|
&.d12-11::before { content : '\f10d'; }
|
||||||
|
&.d12-12::before { content : '\f10e'; }
|
||||||
|
&.d12-2::before { content : '\f10f'; }
|
||||||
|
&.d12-3::before { content : '\f110'; }
|
||||||
|
&.d12-4::before { content : '\f111'; }
|
||||||
|
&.d12-5::before { content : '\f112'; }
|
||||||
|
&.d12-6::before { content : '\f113'; }
|
||||||
|
&.d12-7::before { content : '\f114'; }
|
||||||
|
&.d12-8::before { content : '\f115'; }
|
||||||
|
&.d12-9::before { content : '\f116'; }
|
||||||
|
&.d2::before { content : '\f196'; }
|
||||||
|
&.d2-1::before { content : '\f117'; }
|
||||||
|
&.d2-2::before { content : '\f118'; }
|
||||||
|
&.d20::before { content : '\f197'; }
|
||||||
|
&.d20-1::before { content : '\f119'; }
|
||||||
|
&.d20-10::before { content : '\f11a'; }
|
||||||
|
&.d20-11::before { content : '\f11b'; }
|
||||||
|
&.d20-12::before { content : '\f11c'; }
|
||||||
|
&.d20-13::before { content : '\f11d'; }
|
||||||
|
&.d20-14::before { content : '\f11e'; }
|
||||||
|
&.d20-15::before { content : '\f11f'; }
|
||||||
|
&.d20-16::before { content : '\f120'; }
|
||||||
|
&.d20-17::before { content : '\f121'; }
|
||||||
|
&.d20-18::before { content : '\f122'; }
|
||||||
|
&.d20-19::before { content : '\f123'; }
|
||||||
|
&.d20-2::before { content : '\f124'; }
|
||||||
|
&.d20-20::before { content : '\f125'; }
|
||||||
|
&.d20-3::before { content : '\f126'; }
|
||||||
|
&.d20-4::before { content : '\f127'; }
|
||||||
|
&.d20-5::before { content : '\f128'; }
|
||||||
|
&.d20-6::before { content : '\f129'; }
|
||||||
|
&.d20-7::before { content : '\f12a'; }
|
||||||
|
&.d20-8::before { content : '\f12b'; }
|
||||||
|
&.d20-9::before { content : '\f12c'; }
|
||||||
|
&.d4::before { content : '\f198'; }
|
||||||
|
&.d4-1::before { content : '\f12d'; }
|
||||||
|
&.d4-2::before { content : '\f12e'; }
|
||||||
|
&.d4-3::before { content : '\f12f'; }
|
||||||
|
&.d4-4::before { content : '\f130'; }
|
||||||
|
&.d6::before { content : '\f199'; }
|
||||||
|
&.d6-1::before { content : '\f131'; }
|
||||||
|
&.d6-2::before { content : '\f132'; }
|
||||||
|
&.d6-3::before { content : '\f133'; }
|
||||||
|
&.d6-4::before { content : '\f134'; }
|
||||||
|
&.d6-5::before { content : '\f135'; }
|
||||||
|
&.d6-6::before { content : '\f136'; }
|
||||||
|
&.d8::before { content : '\f19a'; }
|
||||||
|
&.d8-1::before { content : '\f137'; }
|
||||||
|
&.d8-2::before { content : '\f138'; }
|
||||||
|
&.d8-3::before { content : '\f139'; }
|
||||||
|
&.d8-4::before { content : '\f13a'; }
|
||||||
|
&.d8-5::before { content : '\f13b'; }
|
||||||
|
&.d8-6::before { content : '\f13c'; }
|
||||||
|
&.d8-7::before { content : '\f13d'; }
|
||||||
|
&.d8-8::before { content : '\f13e'; }
|
||||||
|
&.dot-d6::before { content : '\f19b'; }
|
||||||
|
&.dot-d6-1::before { content : '\f13f'; }
|
||||||
|
&.dot-d6-2::before { content : '\f140'; }
|
||||||
|
&.dot-d6-3::before { content : '\f141'; }
|
||||||
|
&.dot-d6-4::before { content : '\f142'; }
|
||||||
|
&.dot-d6-5::before { content : '\f143'; }
|
||||||
|
&.dot-d6-6::before { content : '\f18f'; }
|
||||||
|
&.small-dot-d6-1::before { content : '\f183'; }
|
||||||
|
&.small-dot-d6-2::before { content : '\f184'; }
|
||||||
|
&.small-dot-d6-3::before { content : '\f185'; }
|
||||||
|
&.small-dot-d6-4::before { content : '\f186'; }
|
||||||
|
&.small-dot-d6-5::before { content : '\f187'; }
|
||||||
|
&.small-dot-d6-6::before { content : '\f188'; }
|
||||||
|
&.solid-small-dot-d6-1::before { content : '\f189'; }
|
||||||
|
&.solid-small-dot-d6-2::before { content : '\f18a'; }
|
||||||
|
&.solid-small-dot-d6-3::before { content : '\f18b'; }
|
||||||
|
&.solid-small-dot-d6-4::before { content : '\f18c'; }
|
||||||
|
&.solid-small-dot-d6-5::before { content : '\f18d'; }
|
||||||
|
&.solid-small-dot-d6-6::before { content : '\f18e'; }
|
||||||
|
}
|
||||||
208
themes/fonts/iconFonts/elderberryInn.js
Normal file
208
themes/fonts/iconFonts/elderberryInn.js
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
const elderberryInn = {
|
||||||
|
'ei_book' : 'ei book',
|
||||||
|
'ei_screen' : 'ei screen',
|
||||||
|
|
||||||
|
/* Spell levels */
|
||||||
|
'ei_spell_0' : 'ei spell-0',
|
||||||
|
'ei_spell_1' : 'ei spell-1',
|
||||||
|
'ei_spell_2' : 'ei spell-2',
|
||||||
|
'ei_spell_3' : 'ei spell-3',
|
||||||
|
'ei_spell_4' : 'ei spell-4',
|
||||||
|
'ei_spell_5' : 'ei spell-5',
|
||||||
|
'ei_spell_6' : 'ei spell-6',
|
||||||
|
'ei_spell_7' : 'ei spell-7',
|
||||||
|
'ei_spell_8' : 'ei spell-8',
|
||||||
|
'ei_spell_9' : 'ei spell-9',
|
||||||
|
|
||||||
|
/* Damage types */
|
||||||
|
'ei_acid' : 'ei acid',
|
||||||
|
'ei_bludgeoning' : 'ei bludgeoning',
|
||||||
|
'ei_cold' : 'ei cold',
|
||||||
|
'ei_fire' : 'ei fire',
|
||||||
|
'ei_force' : 'ei force',
|
||||||
|
'ei_lightning' : 'ei lightning',
|
||||||
|
'ei_necrotic' : 'ei necrotic',
|
||||||
|
'ei_piercing' : 'ei piercing',
|
||||||
|
'ei_poison' : 'ei poison',
|
||||||
|
'ei_psychic' : 'ei psychic',
|
||||||
|
'ei_radiant' : 'ei radiant',
|
||||||
|
'ei_slashing' : 'ei slashing',
|
||||||
|
'ei_thunder' : 'ei thunder',
|
||||||
|
|
||||||
|
/* DnD Donditions */
|
||||||
|
'ei_blinded' : 'ei blinded',
|
||||||
|
'ei_charmed' : 'ei charmed',
|
||||||
|
'ei_deafened' : 'ei deafened',
|
||||||
|
'ei_exhaust1' : 'ei exhaust-1',
|
||||||
|
'ei_blinded' : 'ei blinded',
|
||||||
|
'ei_exhaust2' : 'ei exhaust-2',
|
||||||
|
'ei_exhaust3' : 'ei exhaust-3',
|
||||||
|
'ei_exhaust4' : 'ei exhaust-4',
|
||||||
|
'ei_exhaust5' : 'ei exhaust-5',
|
||||||
|
'ei_exhaust6' : 'ei exhaust-6',
|
||||||
|
'ei_frightened' : 'ei frightened',
|
||||||
|
'ei_grappled' : 'ei grappled',
|
||||||
|
'ei_incapacitated' : 'ei incapacitated',
|
||||||
|
'ei_invisible' : 'ei invisible',
|
||||||
|
'ei_paralyzed' : 'ei paralyzed',
|
||||||
|
'ei_petrified' : 'ei petrified',
|
||||||
|
'ei_poisoned' : 'ei poisoned',
|
||||||
|
'ei_prone' : 'ei prone',
|
||||||
|
'ei_restrained' : 'ei restrained',
|
||||||
|
'ei_stunned' : 'ei stunned',
|
||||||
|
'ei_unconscious' : 'ei unconscious',
|
||||||
|
|
||||||
|
/* Character Classes and Features */
|
||||||
|
'ei_barbarian_rage' : 'ei barbarian-rage',
|
||||||
|
'ei_barbarian_reckless_attack' : 'ei barbarian-reckless-attack',
|
||||||
|
'ei_bardic_inspiration' : 'ei bardic-inspiration',
|
||||||
|
'ei_cleric_channel_divinity' : 'ei cleric-channel-divinity',
|
||||||
|
'ei_druid_wild_shape' : 'ei druid-wild-shape',
|
||||||
|
'ei_fighter_action_surge' : 'ei fighter-action-surge',
|
||||||
|
'ei_fighter_second_wind' : 'ei fighter-second-wind',
|
||||||
|
'ei_monk_flurry_blows' : 'ei monk-flurry-blows',
|
||||||
|
'ei_monk_patient_defense' : 'ei monk-patient-defense',
|
||||||
|
'ei_monk_step_of_the_wind' : 'ei monk-step-of-the-wind',
|
||||||
|
'ei_monk_step_of_the_wind2' : 'ei monk-step-of-the-wind-2',
|
||||||
|
'ei_monk_step_of_the_wind3' : 'ei monk-step-of-the-wind-3',
|
||||||
|
'ei_monk_stunning_strike' : 'ei monk-stunning-strike',
|
||||||
|
'ei_monk_stunning_strike2' : 'ei monk-stunning-strike-2',
|
||||||
|
'ei_paladin_divine_smite' : 'ei paladin-divine-smite',
|
||||||
|
'ei_paladin_lay_on_hands' : 'ei paladin-lay-on-hands',
|
||||||
|
'ei_barbarian_abilities' : 'ei barbarian-abilities',
|
||||||
|
'ei_barbarian' : 'ei barbarian',
|
||||||
|
'ei_bard_abilities' : 'ei bard-abilities',
|
||||||
|
'ei_bard' : 'ei bard',
|
||||||
|
'ei_cleric_abilities' : 'ei cleric-abilities',
|
||||||
|
'ei_cleric' : 'ei cleric',
|
||||||
|
'ei_druid_abilities' : 'ei druid-abilities',
|
||||||
|
'ei_druid' : 'ei druid',
|
||||||
|
'ei_fighter_abilities' : 'ei fighter-abilities',
|
||||||
|
'ei_fighter' : 'ei fighter',
|
||||||
|
'ei_monk_abilities' : 'ei monk-abilities',
|
||||||
|
'ei_monk' : 'ei monk',
|
||||||
|
'ei_paladin_abilities' : 'ei paladin-abilities',
|
||||||
|
'ei_paladin' : 'ei paladin',
|
||||||
|
'ei_ranger_abilities' : 'ei ranger-abilities',
|
||||||
|
'ei_ranger' : 'ei ranger',
|
||||||
|
'ei_rogue_abilities' : 'ei rogue-abilities',
|
||||||
|
'ei_rogue' : 'ei rogue',
|
||||||
|
'ei_sorcerer_abilities' : 'ei sorcerer-abilities',
|
||||||
|
'ei_sorcerer' : 'ei sorcerer',
|
||||||
|
'ei_warlock_abilities' : 'ei warlock-abilities',
|
||||||
|
'ei_warlock' : 'ei warlock',
|
||||||
|
'ei_wizard_abilities' : 'ei wizard-abilities',
|
||||||
|
'ei_wizard' : 'ei wizard',
|
||||||
|
|
||||||
|
/* Types of actions */
|
||||||
|
'ei_movement' : 'ei movement',
|
||||||
|
'ei_action' : 'ei action',
|
||||||
|
'ei_bonus_action' : 'ei bonus-action',
|
||||||
|
'ei_reaction' : 'ei reaction',
|
||||||
|
|
||||||
|
/* SRD Spells */
|
||||||
|
'ei_acid_arrow' : 'ei acid-arrow',
|
||||||
|
'ei_action1' : 'ei action-1',
|
||||||
|
'ei_alter_self' : 'ei alter-self',
|
||||||
|
'ei_alter_self2' : 'ei alter-self-2',
|
||||||
|
'ei_animal_friendship' : 'ei animal-friendship',
|
||||||
|
'ei_animate_dead' : 'ei animate-dead',
|
||||||
|
'ei_animate_objects' : 'ei animate-objects',
|
||||||
|
'ei_animate_objects2' : 'ei animate-objects-2',
|
||||||
|
'ei_bane' : 'ei bane',
|
||||||
|
'ei_bless' : 'ei bless',
|
||||||
|
'ei_blur' : 'ei blur',
|
||||||
|
'ei_bonus' : 'ei bonus',
|
||||||
|
'ei_branding_smite' : 'ei branding-smite',
|
||||||
|
'ei_burning_hands' : 'ei burning-hands',
|
||||||
|
'ei_charm_person' : 'ei charm-person',
|
||||||
|
'ei_chill_touch' : 'ei chill-touch',
|
||||||
|
'ei_cloudkill' : 'ei cloudkill',
|
||||||
|
'ei_comprehend_languages' : 'ei comprehend-languages',
|
||||||
|
'ei_cone_of_cold' : 'ei cone-of-cold',
|
||||||
|
'ei_conjure_elemental' : 'ei conjure-elemental',
|
||||||
|
'ei_conjure_minor_elemental' : 'ei conjure-minor-elemental',
|
||||||
|
'ei_control_water' : 'ei control-water',
|
||||||
|
'ei_counterspell' : 'ei counterspell',
|
||||||
|
'ei_cure_wounds' : 'ei cure-wounds',
|
||||||
|
'ei_dancing_lights' : 'ei dancing-lights',
|
||||||
|
'ei_darkness' : 'ei darkness',
|
||||||
|
'ei_detect_magic' : 'ei detect-magic',
|
||||||
|
'ei_disguise_self' : 'ei disguise-self',
|
||||||
|
'ei_disintegrate' : 'ei disintegrate',
|
||||||
|
'ei_dispel_evil_and_good' : 'ei dispel-evil-and-good',
|
||||||
|
'ei_dispel_magic' : 'ei dispel-magic',
|
||||||
|
'ei_dominate_monster' : 'ei dominate-monster',
|
||||||
|
'ei_dominate_person' : 'ei dominate-person',
|
||||||
|
'ei_eldritch_blast' : 'ei eldritch-blast',
|
||||||
|
'ei_enlarge_reduce' : 'ei enlarge-reduce',
|
||||||
|
'ei_entangle' : 'ei entangle',
|
||||||
|
'ei_faerie_fire' : 'ei faerie-fire',
|
||||||
|
'ei_faerie_fire2' : 'ei faerie-fire2',
|
||||||
|
'ei_feather_fall' : 'ei feather-fall',
|
||||||
|
'ei_find_familiar' : 'ei find-familiar',
|
||||||
|
'ei_finger_of_death' : 'ei finger-of-death',
|
||||||
|
'ei_fireball' : 'ei fireball',
|
||||||
|
'ei_floating_disk' : 'ei floating-disk',
|
||||||
|
'ei_fly' : 'ei fly',
|
||||||
|
'ei_fog_cloud' : 'ei fog-cloud',
|
||||||
|
'ei_gaseous_form' : 'ei gaseous-form',
|
||||||
|
'ei_gaseous_form2' : 'ei gaseous-form2',
|
||||||
|
'ei_gentle_repose' : 'ei gentle-repose',
|
||||||
|
'ei_gentle_repose2' : 'ei gentle-repose2',
|
||||||
|
'ei_globe_of_invulnerability' : 'ei globe-of-invulnerability',
|
||||||
|
'ei_guiding_bolt' : 'ei guiding-bolt',
|
||||||
|
'ei_healing_word' : 'ei healing-word',
|
||||||
|
'ei_heat_metal' : 'ei heat-metal',
|
||||||
|
'ei_hellish_rebuke' : 'ei hellish-rebuke',
|
||||||
|
'ei_heroes_feast' : 'ei heroes-feast',
|
||||||
|
'ei_heroism' : 'ei heroism',
|
||||||
|
'ei_hideous_laughter' : 'ei hideous-laughter',
|
||||||
|
'ei_identify' : 'ei identify',
|
||||||
|
'ei_illusory_script' : 'ei illusory-script',
|
||||||
|
'ei_inflict_wounds' : 'ei inflict-wounds',
|
||||||
|
'ei_light' : 'ei light',
|
||||||
|
'ei_longstrider' : 'ei longstrider',
|
||||||
|
'ei_mage_armor' : 'ei mage-armor',
|
||||||
|
'ei_mage_hand' : 'ei mage-hand',
|
||||||
|
'ei_magic_missile' : 'ei magic-missile',
|
||||||
|
'ei_mass_cure_wounds' : 'ei mass-cure-wounds',
|
||||||
|
'ei_mass_healing_word' : 'ei mass-healing-word',
|
||||||
|
'ei_mending' : 'ei _mending',
|
||||||
|
'ei_message' : 'ei message',
|
||||||
|
'ei_minor_illusion' : 'ei _minor-illusion',
|
||||||
|
'ei_movement1' : 'ei movement1',
|
||||||
|
'ei_polymorph' : 'ei polymorph',
|
||||||
|
'ei_power_word_kill' : 'ei power-word-kill',
|
||||||
|
'ei_power_word_stun' : 'ei power-word-stun',
|
||||||
|
'ei_prayer_of_healing' : 'ei prayer-of-healing',
|
||||||
|
'ei_prestidigitation' : 'ei prestidigitation',
|
||||||
|
'ei_protection_from_evil_and_good' : 'ei protection-from-evil-and-good',
|
||||||
|
'ei_raise_dead' : 'ei raise-dead',
|
||||||
|
'ei_raise_dead2' : 'ei raise-dead2',
|
||||||
|
'ei_reaction1' : 'ei reaction1',
|
||||||
|
'ei_resurrection' : 'ei resurrection',
|
||||||
|
'ei_resurrection2' : 'ei resurrection2',
|
||||||
|
'ei_revivify' : 'ei revivify',
|
||||||
|
'ei_revivify2' : 'ei revivify2',
|
||||||
|
'ei_sacred_flame' : 'ei sacred-flame',
|
||||||
|
'ei_sanctuary' : 'ei sanctuary',
|
||||||
|
'ei_scorching_ray' : 'ei scorching-ray',
|
||||||
|
'ei_sending' : 'ei sending',
|
||||||
|
'ei_shatter' : 'ei shatter',
|
||||||
|
'ei_shield' : 'ei shield',
|
||||||
|
'ei_silent_image' : 'ei silent-image',
|
||||||
|
'ei_sleep' : 'ei sleep',
|
||||||
|
'ei_speak_with_animals' : 'ei speak-with-animals',
|
||||||
|
'ei_telekinesis' : 'ei telekinesis',
|
||||||
|
'ei_true_strike' : 'ei true-strike',
|
||||||
|
'ei_vicious_mockery' : 'ei vicious-mockery',
|
||||||
|
'ei_wall_of_fire' : 'ei wall-of-fire',
|
||||||
|
'ei_wall_of_force' : 'ei wall-of-force',
|
||||||
|
'ei_wall_of_ice' : 'ei wall-of-ice',
|
||||||
|
'ei_wall_of_stone' : 'ei wall-of-stone',
|
||||||
|
'ei_wall_of_thorns' : 'ei wall-of-thorns',
|
||||||
|
'ei_wish' : 'ei wish'
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = elderberryInn;
|
||||||
222
themes/fonts/iconFonts/elderberryInn.less
Normal file
222
themes/fonts/iconFonts/elderberryInn.less
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
/* Icon Font: Elderberry Inn */
|
||||||
|
@font-face {
|
||||||
|
font-family : 'Elderberry-Inn';
|
||||||
|
font-style : normal;
|
||||||
|
font-weight : normal;
|
||||||
|
src : url('../../../fonts/iconFonts/elderberryInn.woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
.ei {
|
||||||
|
display : inline-block;
|
||||||
|
margin-right : 3px;
|
||||||
|
font-family : 'Elderberry-Inn';
|
||||||
|
line-height : 1;
|
||||||
|
vertical-align : baseline;
|
||||||
|
-moz-osx-font-smoothing : grayscale;
|
||||||
|
-webkit-font-smoothing : antialiased;
|
||||||
|
text-rendering : auto;
|
||||||
|
|
||||||
|
&.book::before { content : '\E900'; }
|
||||||
|
&.screen::before { content : '\E901'; }
|
||||||
|
|
||||||
|
/* Spell levels */
|
||||||
|
&.spell-0::before { content : '\E902'; }
|
||||||
|
&.spell-1::before { content : '\E903'; }
|
||||||
|
&.spell-2::before { content : '\E904'; }
|
||||||
|
&.spell-3::before { content : '\E905'; }
|
||||||
|
&.spell-4::before { content : '\E906'; }
|
||||||
|
&.spell-5::before { content : '\E907'; }
|
||||||
|
&.spell-6::before { content : '\E908'; }
|
||||||
|
&.spell-7::before { content : '\E909'; }
|
||||||
|
&.spell-8::before { content : '\E90A'; }
|
||||||
|
&.spell-9::before { content : '\E90B'; }
|
||||||
|
|
||||||
|
/* Damage types */
|
||||||
|
&.acid::before { content : '\E90C'; }
|
||||||
|
&.bludgeoning::before { content : '\E90D'; }
|
||||||
|
&.cold::before { content : '\E90E'; }
|
||||||
|
&.fire::before { content : '\E90F'; }
|
||||||
|
&.force::before { content : '\E910'; }
|
||||||
|
&.lightning::before { content : '\E911'; }
|
||||||
|
&.necrotic::before { content : '\E912'; }
|
||||||
|
&.piercing::before { content : '\E913'; }
|
||||||
|
&.poison::before { content : '\E914'; }
|
||||||
|
&.psychic::before { content : '\E915'; }
|
||||||
|
&.radiant::before { content : '\E916'; }
|
||||||
|
&.slashing::before { content : '\E917'; }
|
||||||
|
&.thunder::before { content : '\E918'; }
|
||||||
|
|
||||||
|
/* DnD Conditions */
|
||||||
|
&.blinded::before { content : '\E919'; }
|
||||||
|
&.charmed::before { content : '\E91A'; }
|
||||||
|
&.deafened::before { content : '\E91B'; }
|
||||||
|
&.exhaust-1::before { content : '\E91C'; }
|
||||||
|
&.exhaust-2::before { content : '\E91D'; }
|
||||||
|
&.exhaust-3::before { content : '\E91E'; }
|
||||||
|
&.exhaust-4::before { content : '\E91F'; }
|
||||||
|
&.exhaust-5::before { content : '\E920'; }
|
||||||
|
&.exhaust-6::before { content : '\E921'; }
|
||||||
|
&.frightened::before { content : '\E922'; }
|
||||||
|
&.grappled::before { content : '\E923'; }
|
||||||
|
&.incapacitated::before { content : '\E924'; }
|
||||||
|
&.invisible::before { content : '\E926'; }
|
||||||
|
&.paralyzed::before { content : '\E927'; }
|
||||||
|
&.petrified::before { content : '\E928'; }
|
||||||
|
&.poisoned::before { content : '\E929'; }
|
||||||
|
&.prone::before { content : '\E92A'; }
|
||||||
|
&.restrained::before { content : '\E92B'; }
|
||||||
|
&.stunned::before { content : '\E92C'; }
|
||||||
|
&.unconscious::before { content : '\E925'; }
|
||||||
|
|
||||||
|
/* Character Classes and Features */
|
||||||
|
&.barbarian-rage::before { content : '\E92D'; }
|
||||||
|
&.barbarian-reckless-attack::before { content : '\E92E'; }
|
||||||
|
&.bardic-inspiration::before { content : '\E92F'; }
|
||||||
|
&.cleric-channel-divinity::before { content : '\E930'; }
|
||||||
|
&.druid-wild-shape::before { content : '\E931'; }
|
||||||
|
&.fighter-action-surge::before { content : '\E932'; }
|
||||||
|
&.fighter-second-wind::before { content : '\E933'; }
|
||||||
|
&.monk-flurry-blows::before { content : '\E934'; }
|
||||||
|
&.monk-patient-defense::before { content : '\E935'; }
|
||||||
|
&.monk-step-of-the-wind::before { content : '\E936'; }
|
||||||
|
&.monk-step-of-the-wind-2::before { content : '\E937'; }
|
||||||
|
&.monk-step-of-the-wind-3::before { content : '\E938'; }
|
||||||
|
&.monk-stunning-strike::before { content : '\E939'; }
|
||||||
|
&.monk-stunning-strike-2::before { content : '\E939'; }
|
||||||
|
&.paladin-divine-smite::before { content : '\E93B'; }
|
||||||
|
&.paladin-lay-on-hands::before { content : '\E93C'; }
|
||||||
|
&.barbarian-abilities::before { content : '\E93D'; }
|
||||||
|
&.barbarian::before { content : '\E93E'; }
|
||||||
|
&.bard-abilities::before { content : '\E93F'; }
|
||||||
|
&.bard::before { content : '\E940'; }
|
||||||
|
&.cleric-abilities::before { content : '\E941'; }
|
||||||
|
&.cleric::before { content : '\E942'; }
|
||||||
|
&.druid-abilities::before { content : '\E943'; }
|
||||||
|
&.druid::before { content : '\E944'; }
|
||||||
|
&.fighter-abilities::before { content : '\E945'; }
|
||||||
|
&.fighter::before { content : '\E946'; }
|
||||||
|
&.monk-abilities::before { content : '\E947'; }
|
||||||
|
&.monk::before { content : '\E948'; }
|
||||||
|
&.paladin-abilities::before { content : '\E949'; }
|
||||||
|
&.paladin::before { content : '\E94A'; }
|
||||||
|
&.ranger-abilities::before { content : '\E94B'; }
|
||||||
|
&.ranger::before { content : '\E94C'; }
|
||||||
|
&.rogue-abilities::before { content : '\E94D'; }
|
||||||
|
&.rogue::before { content : '\E94E'; }
|
||||||
|
&.sorcerer-abilities::before { content : '\E94F'; }
|
||||||
|
&.sorcerer::before { content : '\E950'; }
|
||||||
|
&.warlock-abilities::before { content : '\E951'; }
|
||||||
|
&.warlock::before { content : '\E952'; }
|
||||||
|
&.wizard-abilities::before { content : '\E953'; }
|
||||||
|
&.wizard::before { content : '\E954'; }
|
||||||
|
|
||||||
|
/* Types of actions */
|
||||||
|
&.movement::before { content : '\E955'; }
|
||||||
|
&.action::before { content : '\E956'; }
|
||||||
|
&.bonus-action::before { content : '\E957'; }
|
||||||
|
&.reaction::before { content : '\E958'; }
|
||||||
|
|
||||||
|
/* SRD Spells */
|
||||||
|
&.acid-arrow::before { content : '\E959'; }
|
||||||
|
&.action-1::before { content : '\E95A'; }
|
||||||
|
&.alter-self::before { content : '\E95B'; }
|
||||||
|
&.alter-self-2::before { content : '\E95C'; }
|
||||||
|
&.animal-friendship::before { content : '\E95E'; }
|
||||||
|
&.animate-dead::before { content : '\E95F'; }
|
||||||
|
&.animate-objects::before { content : '\E960'; }
|
||||||
|
&.animate-objects-2::before { content : '\E961'; }
|
||||||
|
&.bane::before { content : '\E962'; }
|
||||||
|
&.bless::before { content : '\E963'; }
|
||||||
|
&.blur::before { content : '\E964'; }
|
||||||
|
&.bonus::before { content : '\E965'; }
|
||||||
|
&.branding-smite::before { content : '\E966'; }
|
||||||
|
&.burning-hands::before { content : '\E967'; }
|
||||||
|
&.charm-person::before { content : '\E968'; }
|
||||||
|
&.chill-touch::before { content : '\E969'; }
|
||||||
|
&.cloudkill::before { content : '\E96A'; }
|
||||||
|
&.comprehend-languages::before { content : '\E96B'; }
|
||||||
|
&.cone-of-cold::before { content : '\E96C'; }
|
||||||
|
&.conjure-elemental::before { content : '\E96D'; }
|
||||||
|
&.conjure-minor-elemental::before { content : '\E96E'; }
|
||||||
|
&.control-water::before { content : '\E96F'; }
|
||||||
|
&.counterspell::before { content : '\E970'; }
|
||||||
|
&.cure-wounds::before { content : '\E971'; }
|
||||||
|
&.dancing-lights::before { content : '\E972'; }
|
||||||
|
&.darkness::before { content : '\E973'; }
|
||||||
|
&.detect-magic::before { content : '\E974'; }
|
||||||
|
&.disguise-self::before { content : '\E975'; }
|
||||||
|
&.disintegrate::before { content : '\E976'; }
|
||||||
|
&.dispel-evil-and-good::before { content : '\E977'; }
|
||||||
|
&.dispel-magic::before { content : '\E978'; }
|
||||||
|
&.dominate-monster::before { content : '\E979'; }
|
||||||
|
&.dominate-person::before { content : '\E97A'; }
|
||||||
|
&.eldritch-blast::before { content : '\E97B'; }
|
||||||
|
&.enlarge-reduce::before { content : '\E97C'; }
|
||||||
|
&.entangle::before { content : '\E97D'; }
|
||||||
|
&.faerie-fire::before { content : '\E97E'; }
|
||||||
|
&.faerie-fire2::before { content : '\E97F'; }
|
||||||
|
&.feather-fall::before { content : '\E980'; }
|
||||||
|
&.find-familiar::before { content : '\E981'; }
|
||||||
|
&.finger-of-death::before { content : '\E982'; }
|
||||||
|
&.fireball::before { content : '\E983'; }
|
||||||
|
&.floating-disk::before { content : '\E984'; }
|
||||||
|
&.fly::before { content : '\E985'; }
|
||||||
|
&.fog-cloud::before { content : '\E986'; }
|
||||||
|
&.gaseous-form::before { content : '\E987'; }
|
||||||
|
&.gaseous-form2::before { content : '\E988'; }
|
||||||
|
&.gentle-repose::before { content : '\E989'; }
|
||||||
|
&.gentle-repose2::before { content : '\E98A'; }
|
||||||
|
&.globe-of-invulnerability::before { content : '\E98B'; }
|
||||||
|
&.guiding-bolt::before { content : '\E98C'; }
|
||||||
|
&.healing-word::before { content : '\E98D'; }
|
||||||
|
&.heat-metal::before { content : '\E98E'; }
|
||||||
|
&.hellish-rebuke::before { content : '\E98F'; }
|
||||||
|
&.heroes-feast::before { content : '\E990'; }
|
||||||
|
&.heroism::before { content : '\E991'; }
|
||||||
|
&.hideous-laughter::before { content : '\E992'; }
|
||||||
|
&.identify::before { content : '\E993'; }
|
||||||
|
&.illusory-script::before { content : '\E994'; }
|
||||||
|
&.inflict-wounds::before { content : '\E995'; }
|
||||||
|
&.light::before { content : '\E996'; }
|
||||||
|
&.longstrider::before { content : '\E997'; }
|
||||||
|
&.mage-armor::before { content : '\E998'; }
|
||||||
|
&.mage-hand::before { content : '\E999'; }
|
||||||
|
&.magic-missile::before { content : '\E99A'; }
|
||||||
|
&.mass-cure-wounds::before { content : '\E99B'; }
|
||||||
|
&.mass-healing-word::before { content : '\E99C'; }
|
||||||
|
&.Mending::before { content : '\E99D'; }
|
||||||
|
&.message::before { content : '\E99E'; }
|
||||||
|
&.Minor-illusion::before { content : '\E99F'; }
|
||||||
|
&.movement1::before { content : '\E9A0'; }
|
||||||
|
&.polymorph::before { content : '\E9A1'; }
|
||||||
|
&.power-word-kill::before { content : '\E9A2'; }
|
||||||
|
&.power-word-stun::before { content : '\E9A3'; }
|
||||||
|
&.prayer-of-healing::before { content : '\E9A4'; }
|
||||||
|
&.prestidigitation::before { content : '\E9A5'; }
|
||||||
|
&.protection-from-evil-and-good::before { content : '\E9A6'; }
|
||||||
|
&.raise-dead::before { content : '\E9A7'; }
|
||||||
|
&.raise-dead2::before { content : '\E9A8'; }
|
||||||
|
&.reaction1::before { content : '\E9A9'; }
|
||||||
|
&.resurrection::before { content : '\E9AA'; }
|
||||||
|
&.resurrection2::before { content : '\E9AB'; }
|
||||||
|
&.revivify::before { content : '\E9AC'; }
|
||||||
|
&.revivify2::before { content : '\E9AD'; }
|
||||||
|
&.sacred-flame::before { content : '\E9AE'; }
|
||||||
|
&.sanctuary::before { content : '\E9AF'; }
|
||||||
|
&.scorching-ray::before { content : '\E9B0'; }
|
||||||
|
&.sending::before { content : '\E9B1'; }
|
||||||
|
&.shatter::before { content : '\E9B2'; }
|
||||||
|
&.shield::before { content : '\E9B3'; }
|
||||||
|
&.silent-image::before { content : '\E9B4'; }
|
||||||
|
&.sleep::before { content : '\E9B5'; }
|
||||||
|
&.speak-with-animals::before { content : '\E9B6'; }
|
||||||
|
&.telekinesis::before { content : '\E9B7'; }
|
||||||
|
&.true-strike::before { content : '\E9B8'; }
|
||||||
|
&.vicious-mockery::before { content : '\E9B9'; }
|
||||||
|
&.wall-of-fire::before { content : '\E9BA'; }
|
||||||
|
&.wall-of-force::before { content : '\E9BB'; }
|
||||||
|
&.wall-of-ice::before { content : '\E9BC'; }
|
||||||
|
&.wall-of-stone::before { content : '\E9BD'; }
|
||||||
|
&.wall-of-thorns::before { content : '\E9BE'; }
|
||||||
|
&.wish::before { content : '\E9BF'; }
|
||||||
|
}
|
||||||
2054
themes/fonts/iconFonts/fontAwesome.js
Normal file
2054
themes/fonts/iconFonts/fontAwesome.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user