mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-28 04:59:44 +00:00
Compare commits
404 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e38271ac6 | ||
|
|
4866eacd5d | ||
|
|
1386020bbb | ||
|
|
6b7af58e6c | ||
|
|
0f1d07d90f | ||
|
|
d5230757b1 | ||
|
|
06825468b4 | ||
|
|
faab60f271 | ||
|
|
4bed2349a9 | ||
|
|
53f1e53fcb | ||
|
|
df447d3d4d | ||
|
|
e3bf913a80 | ||
|
|
7bb1f16946 | ||
|
|
6870fd6d76 | ||
|
|
9ad1d1f196 | ||
|
|
969cff61bf | ||
|
|
736f729457 | ||
|
|
0050e1e294 | ||
|
|
f2d1b61a7a | ||
|
|
1d1fa99b4b | ||
|
|
f42cab6e40 | ||
|
|
bc21abd509 | ||
|
|
72744718cc | ||
|
|
e85a62a05c | ||
|
|
5a68acc0f5 | ||
|
|
f51fca74e6 | ||
|
|
fe5a76c0df | ||
|
|
3bda834ad3 | ||
|
|
29f0a8e635 | ||
|
|
c035404555 | ||
|
|
74ddc71962 | ||
|
|
1491a1b4ff | ||
|
|
ab54188ba4 | ||
|
|
e0b6b95295 | ||
|
|
c7cfade86f | ||
|
|
e4fa59aae8 | ||
|
|
35227268cf | ||
|
|
c27f5d9efa | ||
|
|
87c9e587a1 | ||
|
|
ed2d539995 | ||
|
|
65cc8567a1 | ||
|
|
dd82f54549 | ||
|
|
a014056440 | ||
|
|
c176c38f30 | ||
|
|
4c87aed628 | ||
|
|
09f2f96dff | ||
|
|
f075b19a68 | ||
|
|
7177548c0e | ||
|
|
3bc2df0ac5 | ||
|
|
77abab8395 | ||
|
|
37c72d5125 | ||
|
|
602a476b59 | ||
|
|
965397733d | ||
|
|
f10868db0b | ||
|
|
d7a95d3cff | ||
|
|
6eeed49022 | ||
|
|
be96c3a56f | ||
|
|
57a31f3b71 | ||
|
|
7c5955c96f | ||
|
|
538650bf92 | ||
|
|
54921a998a | ||
|
|
da11089fff | ||
|
|
0243d138ff | ||
|
|
4c8a5baee5 | ||
|
|
e5acbfed3a | ||
|
|
0163e22567 | ||
|
|
b503b8fc9b | ||
|
|
3243e4d56c | ||
|
|
fbc164a9b8 | ||
|
|
78cf95fbb1 | ||
|
|
d2306b70a9 | ||
|
|
43180c314f | ||
|
|
f47c2dcb56 | ||
|
|
a4ea1612c1 | ||
|
|
8b6517eb8d | ||
|
|
067a7cd507 | ||
|
|
0243b5f491 | ||
|
|
31c7fd12b9 | ||
|
|
7b8aaa408d | ||
|
|
1c65ee150b | ||
|
|
b0cfeaa782 | ||
|
|
84de560083 | ||
|
|
8fc6047127 | ||
|
|
62e679571e | ||
|
|
f32c52a460 | ||
|
|
edbe8cdace | ||
|
|
9e4344de83 | ||
|
|
760269a6e1 | ||
|
|
e53e00713d | ||
|
|
6beee49ebc | ||
|
|
eaf3c7978d | ||
|
|
d36e052478 | ||
|
|
099ea08bd4 | ||
|
|
b0ea34cc3f | ||
|
|
a241813b8d | ||
|
|
de27437148 | ||
|
|
79e8dfec18 | ||
|
|
0c010a0a87 | ||
|
|
0c6c9fce4d | ||
|
|
ac9fc720f7 | ||
|
|
8b31966c2b | ||
|
|
76d966f17d | ||
|
|
d0d8b268d4 | ||
|
|
8bd8f0fc37 | ||
|
|
a2acf3be0e | ||
|
|
844a3c19ec | ||
|
|
877c2e365f | ||
|
|
81c7950ad1 | ||
|
|
7b8e3da90a | ||
|
|
eef3d7738e | ||
|
|
b8ca837c02 | ||
|
|
1fc3573087 | ||
|
|
5ab89b2583 | ||
|
|
539cd1d2b9 | ||
|
|
4424a331d5 | ||
|
|
c4defb7b3f | ||
|
|
c1e17bb6aa | ||
|
|
d648bacd26 | ||
|
|
1ca5ba1086 | ||
|
|
7c28f60e0a | ||
|
|
2fd2ccfe14 | ||
|
|
69308cfd8b | ||
|
|
ccc1895304 | ||
|
|
05a0d80c1b | ||
|
|
2ea65de0c0 | ||
|
|
742798ad79 | ||
|
|
f088fc49f3 | ||
|
|
b837ac5d6b | ||
|
|
64af24f0f4 | ||
|
|
5abb1db512 | ||
|
|
f1133b9c33 | ||
|
|
c73d02c550 | ||
|
|
f1d26cc0c0 | ||
|
|
077d699f0b | ||
|
|
2ed8614642 | ||
|
|
9834fcb97f | ||
|
|
347393520d | ||
|
|
e65e12ff6e | ||
|
|
4dfe1a0914 | ||
|
|
1a1acec2f3 | ||
|
|
e6276a0c7b | ||
|
|
84a0c43745 | ||
|
|
baed640a3d | ||
|
|
7d6851572f | ||
|
|
3e2ec7cd36 | ||
|
|
0ac88bd84a | ||
|
|
db3f9a45ad | ||
|
|
8fc2737670 | ||
|
|
0c2e4ce20b | ||
|
|
62948b2838 | ||
|
|
3dfb3a9738 | ||
|
|
42c1bece65 | ||
|
|
448c12cc91 | ||
|
|
56c82f8793 | ||
|
|
84b0407f74 | ||
|
|
90977521df | ||
|
|
09a52bc7cb | ||
|
|
6ef80eed7f | ||
|
|
ad1795258b | ||
|
|
78c26ab1a3 | ||
|
|
eb669e6eca | ||
|
|
17355012fb | ||
|
|
eae593ce90 | ||
|
|
7a8a6480de | ||
|
|
d0dd61a25c | ||
|
|
dbf0559f95 | ||
|
|
8e88b881fc | ||
|
|
c722e0af39 | ||
|
|
6dcdc1b685 | ||
|
|
76d17baf7e | ||
|
|
c97e2be9d5 | ||
|
|
229acbfcd1 | ||
|
|
2cb216ed7b | ||
|
|
8674bc9da2 | ||
|
|
199e049871 | ||
|
|
31f18ef3d5 | ||
|
|
9db55c4dff | ||
|
|
e2ac6c9b6b | ||
|
|
407c35d9f7 | ||
|
|
8575d72f6e | ||
|
|
7b8e398891 | ||
|
|
89c5c3f255 | ||
|
|
4dd58aaad3 | ||
|
|
3a4de13551 | ||
|
|
a65c24bebf | ||
|
|
9ed32527a6 | ||
|
|
62982f86a1 | ||
|
|
985a9843f2 | ||
|
|
3bbcb1b6fb | ||
|
|
9da203d204 | ||
|
|
212b3f7e05 | ||
|
|
7a06fe386d | ||
|
|
4580217410 | ||
|
|
e82ba8cb7a | ||
|
|
636f10cb41 | ||
|
|
d6eaa812b1 | ||
|
|
507f170720 | ||
|
|
faaf4207b4 | ||
|
|
688377ce0b | ||
|
|
8cf57dbc72 | ||
|
|
12012a2a5b | ||
|
|
9b59f47536 | ||
|
|
90b4e47861 | ||
|
|
ef00231c5b | ||
|
|
c39653bc69 | ||
|
|
83faa86063 | ||
|
|
af20d0b1c2 | ||
|
|
1861c7db69 | ||
|
|
a88b256b6c | ||
|
|
40542e9bec | ||
|
|
04187cf769 | ||
|
|
67a4391dcb | ||
|
|
3784e0f583 | ||
|
|
df33713f82 | ||
|
|
c4ba6381f2 | ||
|
|
547daf6499 | ||
|
|
649e225359 | ||
|
|
35bf26feae | ||
|
|
481bab9463 | ||
|
|
a0e2997a40 | ||
|
|
74c4f4fe52 | ||
|
|
7c9513f377 | ||
|
|
c10be139c9 | ||
|
|
ee4921f02c | ||
|
|
f5f6137a4d | ||
|
|
0775f9ee1b | ||
|
|
75ed555de1 | ||
|
|
e7d0741139 | ||
|
|
c69de1036f | ||
|
|
7d115c970a | ||
|
|
852aa8d289 | ||
|
|
ae974b270d | ||
|
|
ef10b71e56 | ||
|
|
5bfd0dd537 | ||
|
|
05e56221f4 | ||
|
|
5c5230e64e | ||
|
|
769f636db2 | ||
|
|
688eca05e1 | ||
|
|
4c48992331 | ||
|
|
fcb9a8cdc5 | ||
|
|
78d4d6fb7c | ||
|
|
407efd0f8b | ||
|
|
d39ae139d7 | ||
|
|
467b728c47 | ||
|
|
b02036fb7a | ||
|
|
e99aad15c1 | ||
|
|
c57b011215 | ||
|
|
5942bfece1 | ||
|
|
2dc874daba | ||
|
|
38fa428fde | ||
|
|
6bf51cd94a | ||
|
|
ddef21cd7e | ||
|
|
bf30cadb68 | ||
|
|
abe3a7e7c7 | ||
|
|
4ec5f73aed | ||
|
|
a5313deb78 | ||
|
|
5dd486866f | ||
|
|
ada642e56e | ||
|
|
881fcc9cba | ||
|
|
a809e920fc | ||
|
|
36f3eb4da1 | ||
|
|
57772065e0 | ||
|
|
f10be94190 | ||
|
|
055ee38cb7 | ||
|
|
202b275966 | ||
|
|
39e33da2d1 | ||
|
|
71f1aed227 | ||
|
|
159d5a35b2 | ||
|
|
3073b3e35d | ||
|
|
0491516662 | ||
|
|
b55616170b | ||
|
|
e080c46509 | ||
|
|
171d1c7c46 | ||
|
|
d1503c8d6f | ||
|
|
be72b029bf | ||
|
|
93a7b11017 | ||
|
|
045fbbe158 | ||
|
|
ede731e3a5 | ||
|
|
d6e63604ac | ||
|
|
960c03bfa6 | ||
|
|
cda98e02e0 | ||
|
|
9ac070512d | ||
|
|
a1e9c82c06 | ||
|
|
b116e0a622 | ||
|
|
ef2bbfea5e | ||
|
|
c858c705d2 | ||
|
|
fb91761c31 | ||
|
|
93d38eb184 | ||
|
|
d31dae728f | ||
|
|
c068aca9ff | ||
|
|
fcdaef2445 | ||
|
|
8d94e5fbe0 | ||
|
|
c6d8bbae16 | ||
|
|
0a54c7b04e | ||
|
|
2fd54ee87e | ||
|
|
1f5f160964 | ||
|
|
8d04f09aab | ||
|
|
be1f905b48 | ||
|
|
dbbd8cb26d | ||
|
|
7b85995b4a | ||
|
|
37593573ce | ||
|
|
0bdadaf946 | ||
|
|
7b1a815e78 | ||
|
|
7b9a23670d | ||
|
|
a762626c53 | ||
|
|
da2c647fa6 | ||
|
|
cb61badfc5 | ||
|
|
d2f909384e | ||
|
|
cebc7be81d | ||
|
|
83c604cb74 | ||
|
|
ad455f652c | ||
|
|
3f19b2975c | ||
|
|
cacfc788fb | ||
|
|
8f99712a28 | ||
|
|
367c2bd111 | ||
|
|
1c45cb1b7f | ||
|
|
19fbd832f1 | ||
|
|
7ce0ac577a | ||
|
|
3ce4d6d1f8 | ||
|
|
18aca07e84 | ||
|
|
4bda071742 | ||
|
|
35bde09aa7 | ||
|
|
d43ea46e40 | ||
|
|
f1ca6eeee2 | ||
|
|
d390d518a3 | ||
|
|
837306c9a7 | ||
|
|
c58c8777f1 | ||
|
|
caadb7b4ce | ||
|
|
ce946bda98 | ||
|
|
07b1254309 | ||
|
|
4c7715286e | ||
|
|
980c544bba | ||
|
|
7c0b9ea3f6 | ||
|
|
e6d8784633 | ||
|
|
4720aced6c | ||
|
|
cf5e61cf09 | ||
|
|
9d43588f44 | ||
|
|
ce005da20f | ||
|
|
17531151ad | ||
|
|
863f624772 | ||
|
|
6a5f4efd26 | ||
|
|
6200b416ab | ||
|
|
dc1931a5e3 | ||
|
|
26bddc1a79 | ||
|
|
74ebd44d7c | ||
|
|
989af1bbd0 | ||
|
|
3359e489f5 | ||
|
|
f431ac2e40 | ||
|
|
511c38dd1e | ||
|
|
85b0976082 | ||
|
|
7052337669 | ||
|
|
e07d1d1ddb | ||
|
|
c5ebd0352d | ||
|
|
a8fbcf0ad1 | ||
|
|
92f33136ce | ||
|
|
9260283914 | ||
|
|
a08263dd7c | ||
|
|
7eae170f6c | ||
|
|
b5cb8ce834 | ||
|
|
d497c0094b | ||
|
|
71141aa6f6 | ||
|
|
36e607eb3b | ||
|
|
d67f206900 | ||
|
|
6bf9ddd585 | ||
|
|
1607a1ac10 | ||
|
|
392a3db3c2 | ||
|
|
0845234f2f | ||
|
|
f58a7d65b5 | ||
|
|
3ac0ac7568 | ||
|
|
1c71ff0945 | ||
|
|
3ccfef0763 | ||
|
|
a6ec8370be | ||
|
|
7b059f029d | ||
|
|
2de7ec3585 | ||
|
|
efbfbf3568 | ||
|
|
12ca82e6e6 | ||
|
|
a420f202d8 | ||
|
|
3628ca837a | ||
|
|
cc3a42402a | ||
|
|
1e9b71080b | ||
|
|
90917bb84c | ||
|
|
7f6e90fee3 | ||
|
|
b8d8c1bebb | ||
|
|
7ee0e914e6 | ||
|
|
08dbd5638d | ||
|
|
b7d7f4f2a0 | ||
|
|
3cf6691e67 | ||
|
|
8077a91ff7 | ||
|
|
09670b8535 | ||
|
|
36f8f39486 | ||
|
|
1556cf361a | ||
|
|
b557144f63 | ||
|
|
738fc62b8f | ||
|
|
216de73c93 | ||
|
|
a306030635 | ||
|
|
7870c763df | ||
|
|
7f1758364b | ||
|
|
46a6ed4fcc | ||
|
|
7efd23039e | ||
|
|
38c20430a9 | ||
|
|
3d71799469 | ||
|
|
fe402bc211 | ||
|
|
dc7e4cd192 | ||
|
|
7fd7230e15 |
177
changelog.md
177
changelog.md
@@ -75,11 +75,188 @@ pre {
|
|||||||
.page {
|
.page {
|
||||||
padding-bottom: 1.5cm;
|
padding-bottom: 1.5cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.varSyntaxTable th:first-of-type {
|
||||||
|
width:6cm;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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
|
||||||
|
{{taskList
|
||||||
|
|
||||||
|
##### Gazook89
|
||||||
|
|
||||||
|
* [x] Brew view count no longer increases when viewed by owner
|
||||||
|
|
||||||
|
Fixes issue [#3037](https://github.com/naturalcrit/homebrewery/issues/3037)
|
||||||
|
|
||||||
|
* [x] Small tweak to PHB H3 sizing
|
||||||
|
|
||||||
|
Fixes issue [#2989](https://github.com/naturalcrit/homebrewery/issues/2989)
|
||||||
|
|
||||||
|
* [x] Add **Fold/Unfold All** {{fas,fa-compress-alt}} / {{fas,fa-expand-alt}} buttons to editor bar
|
||||||
|
|
||||||
|
Fixes issue [#2965](https://github.com/naturalcrit/homebrewery/issues/2965)
|
||||||
|
|
||||||
|
|
||||||
|
##### G-Ambatte
|
||||||
|
|
||||||
|
* [x] Share link added to Editor Access error page
|
||||||
|
|
||||||
|
Fixes issue [#3086](https://github.com/naturalcrit/homebrewery/issues/3086)
|
||||||
|
|
||||||
|
* [x] Add Darkbrewery theme to Editor theme selector {{fas,fa-palette}}
|
||||||
|
|
||||||
|
Fixes issue [#3034](https://github.com/naturalcrit/homebrewery/issues/3034)
|
||||||
|
|
||||||
|
* [x] Fix Firefox prints with alternating blank pages
|
||||||
|
|
||||||
|
Fixes issue [#3115](https://github.com/naturalcrit/homebrewery/issues/3115)
|
||||||
|
|
||||||
|
* [x] Admin page working again
|
||||||
|
|
||||||
|
Fixes issue [#2657](https://github.com/naturalcrit/homebrewery/issues/2657)
|
||||||
|
|
||||||
|
|
||||||
|
##### 5e-Cleric
|
||||||
|
|
||||||
|
* [x] Fix indenting issue with Monster Blocks and italics in Class Feature
|
||||||
|
|
||||||
|
Fixes issues [#527](https://github.com/naturalcrit/homebrewery/issues/527),
|
||||||
|
[#3247](https://github.com/naturalcrit/homebrewery/issues/3247)
|
||||||
|
|
||||||
|
* [x] Allow CSS vars in curly syntax to be formatted as strings using single quotes
|
||||||
|
|
||||||
|
`{{--customVar:"'a string'"}}`
|
||||||
|
|
||||||
|
Fixes issue [#3066](https://github.com/naturalcrit/homebrewery/issues/3066)
|
||||||
|
|
||||||
|
* [x] Add *Elderberry Inn* icons {{ei,action}} `{{ei,icon-name}}`
|
||||||
|
|
||||||
|
Fixes issue [#3171](https://github.com/naturalcrit/homebrewery/issues/3171)
|
||||||
|
|
||||||
|
* [x] New {{openSans **{{fas,fa-keyboard}} FONTS** }} snippets!
|
||||||
|
|
||||||
|
Fixes issue [#3171](https://github.com/naturalcrit/homebrewery/issues/3171)
|
||||||
|
|
||||||
|
* [x] New page now opens in a new tab
|
||||||
|
|
||||||
|
|
||||||
|
##### abquintic (new contributor!)
|
||||||
|
|
||||||
|
* [x] Add ^super^ `^abc^` and ^^sub^^ `^^abc^^` syntax.
|
||||||
|
|
||||||
|
Fixes issue [#2171](https://github.com/naturalcrit/homebrewery/issues/2171)
|
||||||
|
|
||||||
|
* [x] Add HTML tag assignment to curly syntax `{{tag=value}}`
|
||||||
|
|
||||||
|
Fixes issue [1488](https://github.com/naturalcrit/homebrewery/issues/1488)
|
||||||
|
|
||||||
|
* [x] {{openSans **Brew → Clone to New**}} now clones tags
|
||||||
|
|
||||||
|
Fixes issue [1488](https://github.com/naturalcrit/homebrewery/issues/1488)
|
||||||
|
|
||||||
|
##### calculuschild
|
||||||
|
|
||||||
|
* [x] Better error messages for "Out of Google Drive Storage" and "Not logged in to edit"
|
||||||
|
|
||||||
|
Fixes issues [2510](https://github.com/naturalcrit/homebrewery/issues/2510),
|
||||||
|
[2975](https://github.com/naturalcrit/homebrewery/issues/2975)
|
||||||
|
|
||||||
|
* [x] New Variables syntax. See below for details.
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide
|
||||||
|
|
||||||
|
### 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:
|
||||||
|
|
||||||
|
{{varSyntaxTable
|
||||||
|
| syntax | description |
|
||||||
|
|:-------|-------------|
|
||||||
|
| `[var]:content` | Assigns a variable (must start on a line by itself, and ends at the next blank line) |
|
||||||
|
| `[var](content)` | Assigns a variable and outputs it (can be inline) |
|
||||||
|
| `[var]` | Outputs the variable contents as a link, if formatted as a valid link |
|
||||||
|
| `![var]` | Outputs as an image, if formatted as a valid image |
|
||||||
|
| `$[var]` | Outputs as Markdown |
|
||||||
|
| `$[var1 + var2 - 2 * var3]` | Performs math operations and outputs result if all variables are valid numbers |
|
||||||
|
}}
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,margin-top:0,margin-bottom:0
|
||||||
|
### Examples
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,columns:2,margin-top:0,margin-bottom:0
|
||||||
|
|
||||||
|
```
|
||||||
|
[first]: Bob
|
||||||
|
|
||||||
|
[last]: Jones
|
||||||
|
|
||||||
|
My name is $[first] $[last].
|
||||||
|
```
|
||||||
|
|
||||||
|
\column
|
||||||
|
|
||||||
|
[first]: Bob
|
||||||
|
|
||||||
|
[last]: Jones
|
||||||
|
|
||||||
|
My name is $[first] $[last].
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,columns:2,margin-top:0,margin-bottom:0
|
||||||
|
|
||||||
|
```
|
||||||
|
[myTable]:
|
||||||
|
| h1 | h2 |
|
||||||
|
|----|----|
|
||||||
|
| c1 | c2 |
|
||||||
|
|
||||||
|
Here is my table:
|
||||||
|
$[myTable]
|
||||||
|
```
|
||||||
|
|
||||||
|
\column
|
||||||
|
|
||||||
|
[myTable]:
|
||||||
|
| h1 | h2 |
|
||||||
|
|----|----|
|
||||||
|
| c1 | c2 |
|
||||||
|
|
||||||
|
Here is my table:
|
||||||
|
$[myTable]
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,columns:2,margin-top:0,margin-bottom:0
|
||||||
|
|
||||||
|
```
|
||||||
|
There are $[TableNum] tables total.
|
||||||
|
|
||||||
|
#### Table $[TableNum](1): Horses
|
||||||
|
|
||||||
|
#### Table $[TableNum]($[TableNum + 1]): Cows
|
||||||
|
```
|
||||||
|
|
||||||
|
\column
|
||||||
|
|
||||||
|
There are $[TableNum] tables in this document. *(note: final value of `$[TableNum]` gets hoisted up if available)*
|
||||||
|
|
||||||
|
|
||||||
|
#### Table $[TableNum](1): Horses
|
||||||
|
|
||||||
|
#### Table $[TableNum]($[TableNum + 1]): Cows
|
||||||
|
}}
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Friday 13/10/2023 - v3.10.0
|
### Friday 13/10/2023 - v3.10.0
|
||||||
{{taskList
|
{{taskList
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
||||||
require('./brewRenderer.less');
|
require('./brewRenderer.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const { useState, useRef, useEffect } = React;
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
|
||||||
|
|
||||||
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
|
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
|
||||||
const Markdown = require('naturalcrit/markdown.js');
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
@@ -13,218 +12,200 @@ const ErrorBar = require('./errorBar/errorBar.jsx');
|
|||||||
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
|
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
|
||||||
const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
|
const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
|
||||||
const Frame = require('react-frame-component').default;
|
const Frame = require('react-frame-component').default;
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
const Themes = require('themes/themes.json');
|
const Themes = require('themes/themes.json');
|
||||||
|
|
||||||
const PAGE_HEIGHT = 1056;
|
const PAGE_HEIGHT = 1056;
|
||||||
const PPR_THRESHOLD = 50;
|
|
||||||
|
|
||||||
const BrewRenderer = createClass({
|
const INITIAL_CONTENT = dedent`
|
||||||
displayName : 'BrewRenderer',
|
<!DOCTYPE html><html><head>
|
||||||
getDefaultProps : function() {
|
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />
|
||||||
return {
|
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
||||||
|
<link href='/homebrew/bundle.css' rel='stylesheet' />
|
||||||
|
<base target=_blank>
|
||||||
|
</head><body style='overflow: hidden'><div></div></body></html>`;
|
||||||
|
|
||||||
|
//v=====----------------------< Brew Page Component >---------------------=====v//
|
||||||
|
const BrewPage = (props)=>{
|
||||||
|
props = {
|
||||||
|
contents : '',
|
||||||
|
index : 0,
|
||||||
|
...props
|
||||||
|
};
|
||||||
|
return <div className={props.className} id={`p${props.index + 1}`} >
|
||||||
|
<div className='columnWrapper' dangerouslySetInnerHTML={{ __html: props.contents }} />
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//v=====--------------------< Brew Renderer Component >-------------------=====v//
|
||||||
|
const renderedPages = [];
|
||||||
|
let rawPages = [];
|
||||||
|
|
||||||
|
const BrewRenderer = (props)=>{
|
||||||
|
props = {
|
||||||
text : '',
|
text : '',
|
||||||
style : '',
|
style : '',
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
theme : '5ePHB',
|
theme : '5ePHB',
|
||||||
lang : '',
|
lang : '',
|
||||||
errors : []
|
errors : [],
|
||||||
|
currentEditorPage : 0,
|
||||||
|
...props
|
||||||
};
|
};
|
||||||
},
|
|
||||||
getInitialState : function() {
|
|
||||||
let pages;
|
|
||||||
if(this.props.renderer == 'legacy') {
|
|
||||||
pages = this.props.text.split('\\page');
|
|
||||||
} else {
|
|
||||||
pages = this.props.text.split(/^\\page$/gm);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
const [state, setState] = useState({
|
||||||
viewablePageNumber : 0,
|
viewablePageNumber : 0,
|
||||||
height : 0,
|
height : PAGE_HEIGHT,
|
||||||
isMounted : false,
|
isMounted : false,
|
||||||
|
|
||||||
pages : pages,
|
|
||||||
usePPR : pages.length >= PPR_THRESHOLD,
|
|
||||||
visibility : 'hidden',
|
visibility : 'hidden',
|
||||||
initialContent : `<!DOCTYPE html><html><head>
|
});
|
||||||
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />
|
|
||||||
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
|
||||||
<link href='/homebrew/bundle.css' rel='stylesheet' />
|
|
||||||
<base target=_blank>
|
|
||||||
</head><body style='overflow: hidden'><div></div></body></html>`
|
|
||||||
};
|
|
||||||
},
|
|
||||||
height : 0,
|
|
||||||
lastRender : <div></div>,
|
|
||||||
|
|
||||||
componentWillUnmount : function() {
|
const mainRef = useRef(null);
|
||||||
window.removeEventListener('resize', this.updateSize);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidUpdate : function(prevProps) {
|
if(props.renderer == 'legacy') {
|
||||||
if(prevProps.text !== this.props.text) {
|
rawPages = props.text.split('\\page');
|
||||||
let pages;
|
|
||||||
if(this.props.renderer == 'legacy') {
|
|
||||||
pages = this.props.text.split('\\page');
|
|
||||||
} else {
|
} else {
|
||||||
pages = this.props.text.split(/^\\page$/gm);
|
rawPages = props.text.split(/^\\page$/gm);
|
||||||
}
|
}
|
||||||
this.setState({
|
|
||||||
pages : pages,
|
|
||||||
usePPR : pages.length >= PPR_THRESHOLD
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSize : function() {
|
useEffect(()=>{ // Unmounting steps
|
||||||
this.setState({
|
return ()=>{window.removeEventListener('resize', updateSize);};
|
||||||
height : this.refs.main.parentNode.clientHeight,
|
}, []);
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleScroll : function(e){
|
const updateSize = ()=>{
|
||||||
const target = e.target;
|
setState((prevState)=>({
|
||||||
this.setState((prevState)=>({
|
...prevState,
|
||||||
viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * prevState.pages.length)
|
height : mainRef.current.parentNode.clientHeight,
|
||||||
}));
|
}));
|
||||||
},
|
};
|
||||||
|
|
||||||
shouldRender : function(pageText, index){
|
const handleScroll = (e)=>{
|
||||||
if(!this.state.isMounted) return false;
|
const target = e.target;
|
||||||
|
setState((prevState)=>({
|
||||||
|
...prevState,
|
||||||
|
viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * rawPages.length)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const viewIndex = this.state.viewablePageNumber;
|
const isInView = (index)=>{
|
||||||
if(index == viewIndex - 3) return true;
|
if(!state.isMounted)
|
||||||
if(index == viewIndex - 2) return true;
|
return false;
|
||||||
if(index == viewIndex - 1) return true;
|
|
||||||
if(index == viewIndex) return true;
|
|
||||||
if(index == viewIndex + 1) return true;
|
|
||||||
if(index == viewIndex + 2) return true;
|
|
||||||
if(index == viewIndex + 3) return true;
|
|
||||||
|
|
||||||
//Check for style tages
|
if(index == props.currentEditorPage) //Already rendered before this step
|
||||||
if(pageText.indexOf('<style>') !== -1) return true;
|
return false;
|
||||||
|
|
||||||
|
if(Math.abs(index - state.viewablePageNumber) <= 3)
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
};
|
||||||
|
|
||||||
sanitizeScriptTags : function(content) {
|
const sanitizeScriptTags = (content)=>{
|
||||||
return content
|
return content
|
||||||
.replace(/<script/ig, '<script')
|
.replace(/<script/ig, '<script')
|
||||||
.replace(/<\/script>/ig, '</script>');
|
.replace(/<\/script>/ig, '</script>');
|
||||||
},
|
};
|
||||||
|
|
||||||
renderPageInfo : function(){
|
const renderPageInfo = ()=>{
|
||||||
return <div className='pageInfo' ref='main'>
|
return <div className='pageInfo' ref={mainRef}>
|
||||||
<div>
|
<div>
|
||||||
{this.props.renderer}
|
{props.renderer}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{this.state.viewablePageNumber + 1} / {this.state.pages.length}
|
{state.viewablePageNumber + 1} / {rawPages.length}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
};
|
||||||
|
|
||||||
renderPPRmsg : function(){
|
const renderDummyPage = (index)=>{
|
||||||
if(!this.state.usePPR) return;
|
|
||||||
|
|
||||||
return <div className='ppr_msg'>
|
|
||||||
Partial Page Renderer is enabled, because your brew is so large. May affect rendering.
|
|
||||||
</div>;
|
|
||||||
},
|
|
||||||
|
|
||||||
renderDummyPage : function(index){
|
|
||||||
return <div className='phb page' id={`p${index + 1}`} key={index}>
|
return <div className='phb page' id={`p${index + 1}`} key={index}>
|
||||||
<i className='fas fa-spinner fa-spin' />
|
<i className='fas fa-spinner fa-spin' />
|
||||||
</div>;
|
</div>;
|
||||||
},
|
};
|
||||||
|
|
||||||
renderStyle : function() {
|
const renderStyle = ()=>{
|
||||||
if(!this.props.style) return;
|
if(!props.style) return;
|
||||||
const cleanStyle = this.sanitizeScriptTags(this.props.style);
|
const cleanStyle = sanitizeScriptTags(props.style);
|
||||||
//return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>@layer styleTab {\n${this.sanitizeScriptTags(this.props.style)}\n} </style>` }} />;
|
//return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>@layer styleTab {\n${sanitizeScriptTags(props.style)}\n} </style>` }} />;
|
||||||
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${cleanStyle} </style>` }} />;
|
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${cleanStyle} </style>` }} />;
|
||||||
},
|
};
|
||||||
|
|
||||||
renderPage : function(pageText, index){
|
const renderPage = (pageText, index)=>{
|
||||||
let cleanPageText = this.sanitizeScriptTags(pageText);
|
let cleanPageText = sanitizeScriptTags(pageText);
|
||||||
if(this.props.renderer == 'legacy')
|
if(props.renderer == 'legacy') {
|
||||||
return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(cleanPageText) }} key={index} />;
|
const html = MarkdownLegacy.render(cleanPageText);
|
||||||
else {
|
return <BrewPage className='page phb' index={index} key={index} contents={html} />;
|
||||||
|
} else {
|
||||||
cleanPageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
|
cleanPageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
|
||||||
return (
|
const html = Markdown.render(cleanPageText, index);
|
||||||
<div className='page' id={`p${index + 1}`} key={index} >
|
return <BrewPage className='page' index={index} key={index} contents={html} />;
|
||||||
<div className='columnWrapper' dangerouslySetInnerHTML={{ __html: Markdown.render(cleanPageText) }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
renderPages : function(){
|
const renderPages = ()=>{
|
||||||
if(this.state.usePPR){
|
if(props.errors && props.errors.length)
|
||||||
return _.map(this.state.pages, (page, index)=>{
|
return renderedPages;
|
||||||
if(this.shouldRender(page, index) && typeof window !== 'undefined'){
|
|
||||||
return this.renderPage(page, index);
|
if(rawPages.length != renderedPages.length) // Re-render all pages when page count changes
|
||||||
} else {
|
renderedPages.length = 0;
|
||||||
return this.renderDummyPage(index);
|
|
||||||
|
// Render currently-edited page first so cross-page effects (variables, links) can propagate out first
|
||||||
|
renderedPages[props.currentEditorPage] = renderPage(rawPages[props.currentEditorPage], props.currentEditorPage);
|
||||||
|
|
||||||
|
_.forEach(rawPages, (page, index)=>{
|
||||||
|
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
||||||
|
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
return renderedPages;
|
||||||
if(this.props.errors && this.props.errors.length) return this.lastRender;
|
};
|
||||||
this.lastRender = _.map(this.state.pages, (page, index)=>{
|
|
||||||
if(typeof window !== 'undefined') {
|
|
||||||
return this.renderPage(page, index);
|
|
||||||
} else {
|
|
||||||
return this.renderDummyPage(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return this.lastRender;
|
|
||||||
},
|
|
||||||
|
|
||||||
frameDidMount : function(){ //This triggers when iFrame finishes internal "componentDidMount"
|
const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount"
|
||||||
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
|
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
|
||||||
this.updateSize();
|
updateSize();
|
||||||
window.addEventListener('resize', this.updateSize);
|
window.addEventListener('resize', updateSize);
|
||||||
this.renderPages(); //Make sure page is renderable before showing
|
renderPages(); //Make sure page is renderable before showing
|
||||||
this.setState({
|
setState((prevState)=>({
|
||||||
|
...prevState,
|
||||||
isMounted : true,
|
isMounted : true,
|
||||||
visibility : 'visible'
|
visibility : 'visible'
|
||||||
});
|
}));
|
||||||
}, 100);
|
}, 100);
|
||||||
},
|
};
|
||||||
|
|
||||||
emitClick : function(){
|
const emitClick = ()=>{ // Allow clicks inside iFrame to interact with dropdowns, etc. from outside
|
||||||
// console.log('iFrame clicked');
|
|
||||||
if(!window || !document) return;
|
if(!window || !document) return;
|
||||||
document.dispatchEvent(new MouseEvent('click'));
|
document.dispatchEvent(new MouseEvent('click'));
|
||||||
},
|
};
|
||||||
|
|
||||||
render : function(){
|
const rendererPath = props.renderer == 'V3' ? 'V3' : 'Legacy';
|
||||||
//render in iFrame so broken code doesn't crash the site.
|
const themePath = props.theme ?? '5ePHB';
|
||||||
//Also render dummy page while iframe is mounting.
|
|
||||||
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
|
||||||
const themePath = this.props.theme ?? '5ePHB';
|
|
||||||
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<>
|
||||||
{!this.state.isMounted
|
{/*render dummy page while iFrame is mounting.*/}
|
||||||
? <div className='brewRenderer' onScroll={this.handleScroll}>
|
{!state.isMounted
|
||||||
<div className='pages' ref='pages'>
|
? <div className='brewRenderer' onScroll={handleScroll}>
|
||||||
{this.renderDummyPage(1)}
|
<div className='pages'>
|
||||||
|
{renderDummyPage(1)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
<Frame id='BrewRenderer' initialContent={this.state.initialContent}
|
{/*render in iFrame so broken code doesn't crash the site.*/}
|
||||||
style={{ width: '100%', height: '100%', visibility: this.state.visibility }}
|
<Frame id='BrewRenderer' initialContent={INITIAL_CONTENT}
|
||||||
contentDidMount={this.frameDidMount}
|
style={{ width: '100%', height: '100%', visibility: state.visibility }}
|
||||||
onClick={()=>{this.emitClick();}}
|
contentDidMount={frameDidMount}
|
||||||
|
onClick={()=>{emitClick();}}
|
||||||
>
|
>
|
||||||
<div className={'brewRenderer'}
|
<div className={'brewRenderer'}
|
||||||
onScroll={this.handleScroll}
|
onScroll={handleScroll}
|
||||||
style={{ height: this.state.height }}>
|
style={{ height: state.height }}>
|
||||||
|
|
||||||
<ErrorBar errors={this.props.errors} />
|
<ErrorBar errors={props.errors} />
|
||||||
<div className='popups'>
|
<div className='popups'>
|
||||||
<RenderWarnings />
|
<RenderWarnings />
|
||||||
<NotificationPopup />
|
<NotificationPopup />
|
||||||
@@ -234,23 +215,22 @@ const BrewRenderer = createClass({
|
|||||||
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} rel='stylesheet'/>
|
||||||
}
|
}
|
||||||
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
||||||
|
|
||||||
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
||||||
{this.state.isMounted
|
{state.isMounted
|
||||||
&&
|
&&
|
||||||
<>
|
<>
|
||||||
{this.renderStyle()}
|
{renderStyle()}
|
||||||
<div className='pages' ref='pages' lang={`${this.props.lang || 'en'}`}>
|
<div className='pages' lang={`${props.lang || 'en'}`}>
|
||||||
{this.renderPages()}
|
{renderPages()}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Frame>
|
</Frame>
|
||||||
{this.renderPageInfo()}
|
{renderPageInfo()}
|
||||||
{this.renderPPRmsg()}
|
</>
|
||||||
</React.Fragment>
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = BrewRenderer;
|
module.exports = BrewRenderer;
|
||||||
|
|||||||
@@ -3,44 +3,42 @@
|
|||||||
.brewRenderer {
|
.brewRenderer {
|
||||||
will-change : transform;
|
will-change : transform;
|
||||||
overflow-y : scroll;
|
overflow-y : scroll;
|
||||||
.pages{
|
:where(.pages) {
|
||||||
margin : 30px 0px;
|
margin : 30px 0px;
|
||||||
&>.page{
|
& > :where(.page) {
|
||||||
|
width : 215.9mm;
|
||||||
|
height : 279.4mm;
|
||||||
margin-right : auto;
|
margin-right : auto;
|
||||||
margin-bottom : 30px;
|
margin-bottom : 30px;
|
||||||
margin-left : auto;
|
margin-left : auto;
|
||||||
box-shadow : 1px 4px 14px #000;
|
box-shadow : 1px 4px 14px #000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pane{
|
.pane { position : relative; }
|
||||||
position : relative;
|
|
||||||
}
|
|
||||||
.pageInfo {
|
.pageInfo {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
right : 17px;
|
right : 17px;
|
||||||
bottom : 0;
|
bottom : 0;
|
||||||
z-index : 1000;
|
z-index : 1000;
|
||||||
background-color : #333;
|
|
||||||
font-size : 10px;
|
font-size : 10px;
|
||||||
font-weight : 800;
|
font-weight : 800;
|
||||||
color : white;
|
color : white;
|
||||||
|
background-color : #333333;
|
||||||
div {
|
div {
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
padding : 8px 10px;
|
padding : 8px 10px;
|
||||||
&:not(:last-child){
|
&:not(:last-child) { border-right : 1px solid #666666; }
|
||||||
border-right: 1px solid #666;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ppr_msg {
|
.ppr_msg {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
left : 0px;
|
|
||||||
bottom : 0;
|
bottom : 0;
|
||||||
|
left : 0px;
|
||||||
z-index : 1000;
|
z-index : 1000;
|
||||||
padding : 8px 10px;
|
padding : 8px 10px;
|
||||||
background-color : #333;
|
|
||||||
font-size : 10px;
|
font-size : 10px;
|
||||||
font-weight : 800;
|
font-weight : 800;
|
||||||
color : white;
|
color : white;
|
||||||
|
background-color : #333333;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,10 @@ const NotificationPopup = createClass({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<li key='psa'>
|
<li key='psa'>
|
||||||
<em>Broken default logo on <b>CoverPage</b> </em> <br />
|
<em>Don't store IMAGES in Google Drive</em><br />
|
||||||
If you have used the Cover Page snippet and notice the Naturalcrit
|
Google Drive is not an image service, and will block images from being used
|
||||||
logo is showing as a broken image, this is due to some small tweaks
|
in brews if they get more views than expected. Google has confirmed they won't fix
|
||||||
of this BETA feature. To fix the logo in your cover page, rename
|
this, so we recommend you look for another image hosting service such as imgur, ImgBB or Google Photos.
|
||||||
the image link <b>"/assets/naturalCritLogoRed.svg"</b>. Remember
|
|
||||||
that any snippet marked "BETA" may have a similar change in the
|
|
||||||
future as we encounter any bugs or reworks.
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li key='googleDriveFolder'>
|
<li key='googleDriveFolder'>
|
||||||
|
|||||||
@@ -160,9 +160,27 @@ const Editor = createClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Superscript
|
||||||
|
if(line.includes('\^')) {
|
||||||
|
const regex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/g;
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(line)) != null) {
|
||||||
|
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 1 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 1 }, { className: 'superscript' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscript
|
||||||
|
if(line.includes('^^')) {
|
||||||
|
const regex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/g;
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(line)) != null) {
|
||||||
|
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 2 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 2 }, { className: 'subscript' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Highlight injectors {style}
|
// Highlight injectors {style}
|
||||||
if(line.includes('{') && line.includes('}')){
|
if(line.includes('{') && line.includes('}')){
|
||||||
const regex = /(?:^|[^{\n])({(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\2})/gm;
|
const regex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gm;
|
||||||
let match;
|
let match;
|
||||||
while ((match = regex.exec(line)) != null) {
|
while ((match = regex.exec(line)) != null) {
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'injection' });
|
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'injection' });
|
||||||
@@ -170,7 +188,7 @@ const Editor = createClass({
|
|||||||
}
|
}
|
||||||
// Highlight inline spans {{content}}
|
// Highlight inline spans {{content}}
|
||||||
if(line.includes('{{') && line.includes('}}')){
|
if(line.includes('{{') && line.includes('}}')){
|
||||||
const regex = /{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *|}}/g;
|
const regex = /{{(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\1 *|}}/g;
|
||||||
let match;
|
let match;
|
||||||
let blockCount = 0;
|
let blockCount = 0;
|
||||||
while ((match = regex.exec(line)) != null) {
|
while ((match = regex.exec(line)) != null) {
|
||||||
@@ -189,7 +207,7 @@ const Editor = createClass({
|
|||||||
// Highlight block divs {{\n Content \n}}
|
// Highlight block divs {{\n Content \n}}
|
||||||
let endCh = line.length+1;
|
let endCh = line.length+1;
|
||||||
|
|
||||||
const match = line.match(/^ *{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *$|^ *}}$/);
|
const match = line.match(/^ *{{(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\1 *$|^ *}}$/);
|
||||||
if(match)
|
if(match)
|
||||||
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' });
|
||||||
@@ -341,6 +359,14 @@ const Editor = createClass({
|
|||||||
return this.refs.codeEditor?.undo();
|
return this.refs.codeEditor?.undo();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
foldCode : function(){
|
||||||
|
return this.refs.codeEditor?.foldAllCode();
|
||||||
|
},
|
||||||
|
|
||||||
|
unfoldCode : function(){
|
||||||
|
return this.refs.codeEditor?.unfoldAllCode();
|
||||||
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return (
|
return (
|
||||||
<div className='editor' ref='main'>
|
<div className='editor' ref='main'>
|
||||||
@@ -354,6 +380,8 @@ const Editor = createClass({
|
|||||||
theme={this.props.brew.theme}
|
theme={this.props.brew.theme}
|
||||||
undo={this.undo}
|
undo={this.undo}
|
||||||
redo={this.redo}
|
redo={this.redo}
|
||||||
|
foldCode={this.foldCode}
|
||||||
|
unfoldCode={this.unfoldCode}
|
||||||
historySize={this.historySize()}
|
historySize={this.historySize()}
|
||||||
currentEditorTheme={this.state.editorTheme}
|
currentEditorTheme={this.state.editorTheme}
|
||||||
updateEditorTheme={this.updateEditorTheme}
|
updateEditorTheme={this.updateEditorTheme}
|
||||||
|
|||||||
@@ -43,6 +43,18 @@
|
|||||||
font-weight : bold;
|
font-weight : bold;
|
||||||
color : green;
|
color : green;
|
||||||
}
|
}
|
||||||
|
.superscript:not(.cm-comment) {
|
||||||
|
font-weight : bold;
|
||||||
|
color : goldenrod;
|
||||||
|
vertical-align : super;
|
||||||
|
font-size : 0.9em;
|
||||||
|
}
|
||||||
|
.subscript:not(.cm-comment) {
|
||||||
|
font-weight : bold;
|
||||||
|
color : rgb(123, 123, 15);
|
||||||
|
vertical-align : sub;
|
||||||
|
font-size : 0.9em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.brewJump {
|
.brewJump {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
.metadataEditor{
|
.metadataEditor{
|
||||||
position : absolute;
|
position : absolute;
|
||||||
z-index : 10000;
|
z-index : 5;
|
||||||
box-sizing : border-box;
|
box-sizing : border-box;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
padding : 25px;
|
padding : 25px;
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ const Snippetbar = createClass({
|
|||||||
undo : ()=>{},
|
undo : ()=>{},
|
||||||
redo : ()=>{},
|
redo : ()=>{},
|
||||||
historySize : ()=>{},
|
historySize : ()=>{},
|
||||||
|
foldCode : ()=>{},
|
||||||
|
unfoldCode : ()=>{},
|
||||||
updateEditorTheme : ()=>{},
|
updateEditorTheme : ()=>{},
|
||||||
cursorPos : {}
|
cursorPos : {}
|
||||||
};
|
};
|
||||||
@@ -72,6 +74,7 @@ const Snippetbar = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
mergeCustomizer : function(valueA, valueB, key) {
|
mergeCustomizer : function(valueA, valueB, key) {
|
||||||
if(key == 'snippets') {
|
if(key == 'snippets') {
|
||||||
const result = _.reverse(_.unionBy(_.reverse(valueB), _.reverse(valueA), 'name')); // Join snippets together, with preference for the current theme over the base theme
|
const result = _.reverse(_.unionBy(_.reverse(valueB), _.reverse(valueA), 'name')); // Join snippets together, with preference for the current theme over the base theme
|
||||||
@@ -100,10 +103,12 @@ const Snippetbar = createClass({
|
|||||||
this.props.onInject(injectedText);
|
this.props.onInject(injectedText);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleThemeSelector : function(){
|
toggleThemeSelector : function(e){
|
||||||
|
if(e.target.tagName != 'SELECT'){
|
||||||
this.setState({
|
this.setState({
|
||||||
themeSelector : !this.state.themeSelector
|
themeSelector : !this.state.themeSelector
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
changeTheme : function(e){
|
changeTheme : function(e){
|
||||||
@@ -117,7 +122,7 @@ const Snippetbar = createClass({
|
|||||||
|
|
||||||
renderThemeSelector : function(){
|
renderThemeSelector : function(){
|
||||||
return <div className='themeSelector'>
|
return <div className='themeSelector'>
|
||||||
<select value={this.props.currentEditorTheme} onChange={this.changeTheme} onMouseDown={(this.changeTheme)}>
|
<select value={this.props.currentEditorTheme} onChange={this.changeTheme} >
|
||||||
{EditorThemes.map((theme, key)=>{
|
{EditorThemes.map((theme, key)=>{
|
||||||
return <option key={key} value={theme}>{theme}</option>;
|
return <option key={key} value={theme}>{theme}</option>;
|
||||||
})}
|
})}
|
||||||
@@ -144,6 +149,22 @@ const Snippetbar = createClass({
|
|||||||
renderEditorButtons : function(){
|
renderEditorButtons : function(){
|
||||||
if(!this.props.showEditButtons) return;
|
if(!this.props.showEditButtons) return;
|
||||||
|
|
||||||
|
let foldButtons;
|
||||||
|
if(this.props.view == 'text'){
|
||||||
|
foldButtons =
|
||||||
|
<>
|
||||||
|
<div className={`editorTool foldAll ${this.props.foldCode ? 'active' : ''}`}
|
||||||
|
onClick={this.props.foldCode} >
|
||||||
|
<i className='fas fa-compress-alt' />
|
||||||
|
</div>
|
||||||
|
<div className={`editorTool unfoldAll ${this.props.unfoldCode ? 'active' : ''}`}
|
||||||
|
onClick={this.props.unfoldCode} >
|
||||||
|
<i className='fas fa-expand-alt' />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return <div className='editors'>
|
return <div className='editors'>
|
||||||
<div className={`editorTool undo ${this.props.historySize.undo ? 'active' : ''}`}
|
<div className={`editorTool undo ${this.props.historySize.undo ? 'active' : ''}`}
|
||||||
onClick={this.props.undo} >
|
onClick={this.props.undo} >
|
||||||
@@ -154,11 +175,13 @@ const Snippetbar = createClass({
|
|||||||
<i className='fas fa-redo' />
|
<i className='fas fa-redo' />
|
||||||
</div>
|
</div>
|
||||||
<div className='divider'></div>
|
<div className='divider'></div>
|
||||||
|
{foldButtons}
|
||||||
<div className={`editorTool editorTheme ${this.state.themeSelector ? 'active' : ''}`}
|
<div className={`editorTool editorTheme ${this.state.themeSelector ? 'active' : ''}`}
|
||||||
onClick={this.toggleThemeSelector} >
|
onClick={this.toggleThemeSelector} >
|
||||||
<i className='fas fa-palette' />
|
<i className='fas fa-palette' />
|
||||||
</div>
|
|
||||||
{this.state.themeSelector && this.renderThemeSelector()}
|
{this.state.themeSelector && this.renderThemeSelector()}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='divider'></div>
|
<div className='divider'></div>
|
||||||
<div className={cx('text', { selected: this.props.view === 'text' })}
|
<div className={cx('text', { selected: this.props.view === 'text' })}
|
||||||
onClick={()=>this.props.onViewChange('text')}>
|
onClick={()=>this.props.onViewChange('text')}>
|
||||||
@@ -209,7 +232,7 @@ const SnippetGroup = createClass({
|
|||||||
return _.map(snippets, (snippet)=>{
|
return _.map(snippets, (snippet)=>{
|
||||||
return <div className='snippet' key={snippet.name} onClick={(e)=>this.handleSnippetClick(e, snippet)}>
|
return <div className='snippet' key={snippet.name} onClick={(e)=>this.handleSnippetClick(e, snippet)}>
|
||||||
<i className={snippet.icon} />
|
<i className={snippet.icon} />
|
||||||
<span className='name'>{snippet.name}</span>
|
<span className='name'title={snippet.name}>{snippet.name}</span>
|
||||||
{snippet.experimental && <span className='beta'>beta</span>}
|
{snippet.experimental && <span className='beta'>beta</span>}
|
||||||
{snippet.subsnippets && <>
|
{snippet.subsnippets && <>
|
||||||
<i className='fas fa-caret-right'></i>
|
<i className='fas fa-caret-right'></i>
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
@import (less) './client/icons/customIcons.less';
|
@import (less) './client/icons/customIcons.less';
|
||||||
|
@import (less) '././././themes/fonts/5e/fonts.less';
|
||||||
|
|
||||||
.snippetBar {
|
.snippetBar {
|
||||||
@menuHeight : 25px;
|
@menuHeight : 25px;
|
||||||
position : relative;
|
position : relative;
|
||||||
height : @menuHeight;
|
height : @menuHeight;
|
||||||
background-color : #ddd;
|
color : black;
|
||||||
|
background-color : #DDDDDD;
|
||||||
|
|
||||||
.editors {
|
.editors {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
display : flex;
|
|
||||||
top : 0px;
|
top : 0px;
|
||||||
right : 0px;
|
right : 0px;
|
||||||
height : @menuHeight;
|
display : flex;
|
||||||
width : 125px;
|
|
||||||
justify-content : space-between;
|
justify-content : space-between;
|
||||||
&>div{
|
|
||||||
height : @menuHeight;
|
height : @menuHeight;
|
||||||
|
& > div {
|
||||||
width : @menuHeight;
|
width : @menuHeight;
|
||||||
cursor : pointer;
|
height : @menuHeight;
|
||||||
line-height : @menuHeight;
|
line-height : @menuHeight;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
&:hover,&.selected{
|
cursor : pointer;
|
||||||
background-color : #999;
|
&:hover,&.selected { background-color : #999999; }
|
||||||
}
|
|
||||||
&.text {
|
&.text {
|
||||||
.tooltipLeft('Brew Editor');
|
.tooltipLeft('Brew Editor');
|
||||||
}
|
}
|
||||||
@@ -34,61 +35,66 @@
|
|||||||
.tooltipLeft('Undo');
|
.tooltipLeft('Undo');
|
||||||
font-size : 0.75em;
|
font-size : 0.75em;
|
||||||
color : grey;
|
color : grey;
|
||||||
&.active{
|
&.active { color : inherit; }
|
||||||
color : black;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&.redo {
|
&.redo {
|
||||||
.tooltipLeft('Redo');
|
.tooltipLeft('Redo');
|
||||||
font-size : 0.75em;
|
font-size : 0.75em;
|
||||||
color : grey;
|
color : grey;
|
||||||
&.active{
|
&.active { color : inherit; }
|
||||||
color : black;
|
|
||||||
}
|
}
|
||||||
|
&.foldAll {
|
||||||
|
.tooltipLeft('Fold All');
|
||||||
|
font-size : 0.75em;
|
||||||
|
color : inherit;
|
||||||
|
}
|
||||||
|
&.unfoldAll {
|
||||||
|
.tooltipLeft('Unfold All');
|
||||||
|
font-size : 0.75em;
|
||||||
|
color : inherit;
|
||||||
}
|
}
|
||||||
&.editorTheme {
|
&.editorTheme {
|
||||||
.tooltipLeft('Editor Themes');
|
.tooltipLeft('Editor Themes');
|
||||||
font-size : 0.75em;
|
font-size : 0.75em;
|
||||||
color : black;
|
color : black;
|
||||||
&.active {
|
&.active {
|
||||||
color : white;
|
position : relative;
|
||||||
background-color: black;
|
background-color : #999999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.divider {
|
&.divider {
|
||||||
background: linear-gradient(#000, #000) no-repeat center/1px 100%;
|
|
||||||
width : 5px;
|
width : 5px;
|
||||||
&:hover{
|
background : linear-gradient(currentColor, currentColor) no-repeat center/1px 100%;
|
||||||
background-color: inherit;
|
&:hover { background-color : inherit; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.themeSelector {
|
.themeSelector {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
left: -65px;
|
top : 25px;
|
||||||
top: 30px;
|
right : 0;
|
||||||
z-index: 999;
|
z-index : 10;
|
||||||
|
display : flex;
|
||||||
|
align-items : center;
|
||||||
|
justify-content : center;
|
||||||
width : 170px;
|
width : 170px;
|
||||||
background-color: black;
|
height : inherit;
|
||||||
border-radius: 5px;
|
background-color : inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.snippetBarButton {
|
.snippetBarButton {
|
||||||
height : @menuHeight;
|
|
||||||
line-height : @menuHeight;
|
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
|
height : @menuHeight;
|
||||||
padding : 0px 5px;
|
padding : 0px 5px;
|
||||||
font-weight : 800;
|
|
||||||
font-size : 0.625em;
|
font-size : 0.625em;
|
||||||
|
font-weight : 800;
|
||||||
|
line-height : @menuHeight;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
cursor : pointer;
|
cursor : pointer;
|
||||||
&:hover, &.selected{
|
&:hover, &.selected { background-color : #999999; }
|
||||||
background-color : #999;
|
|
||||||
}
|
|
||||||
i {
|
i {
|
||||||
vertical-align : middle;
|
|
||||||
margin-right : 3px;
|
margin-right : 3px;
|
||||||
font-size : 1.4em;
|
font-size : 1.4em;
|
||||||
|
vertical-align : middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toggleMeta {
|
.toggleMeta {
|
||||||
@@ -96,63 +102,85 @@
|
|||||||
top : 0px;
|
top : 0px;
|
||||||
right : 0px;
|
right : 0px;
|
||||||
border-left : 1px solid black;
|
border-left : 1px solid black;
|
||||||
.tooltipLeft("Edit Brew Properties");
|
.tooltipLeft('Edit Brew Properties');
|
||||||
}
|
}
|
||||||
.snippetGroup {
|
.snippetGroup {
|
||||||
border-right : 1px solid black;
|
border-right : 1px solid currentColor;
|
||||||
&:hover {
|
&:hover {
|
||||||
&>.dropdown{
|
& > .dropdown { visibility : visible; }
|
||||||
visibility : visible;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.dropdown {
|
.dropdown {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
top : 100%;
|
top : 100%;
|
||||||
visibility : hidden;
|
|
||||||
z-index : 1000;
|
z-index : 1000;
|
||||||
margin-left : -5px;
|
|
||||||
padding : 0px;
|
padding : 0px;
|
||||||
background-color : #ddd;
|
margin-left : -5px;
|
||||||
|
visibility : hidden;
|
||||||
|
background-color : #DDDDDD;
|
||||||
.snippet {
|
.snippet {
|
||||||
position : relative;
|
position : relative;
|
||||||
.animate(background-color);
|
|
||||||
display : flex;
|
display : flex;
|
||||||
align-items : center;
|
align-items : center;
|
||||||
min-width : max-content;
|
min-width : max-content;
|
||||||
padding : 5px;
|
padding : 5px;
|
||||||
cursor : pointer;
|
|
||||||
font-size : 10px;
|
font-size : 10px;
|
||||||
|
cursor : pointer;
|
||||||
|
.animate(background-color);
|
||||||
i {
|
i {
|
||||||
|
height : 1.2em;
|
||||||
margin-right : 8px;
|
margin-right : 8px;
|
||||||
font-size : 1.2em;
|
font-size : 1.2em;
|
||||||
height : 1.2em;
|
|
||||||
& ~ i {
|
& ~ i {
|
||||||
margin-right : 0;
|
margin-right : 0;
|
||||||
margin-left : 5px;
|
margin-left : 5px;
|
||||||
}
|
}
|
||||||
|
/* Fonts */
|
||||||
|
&.font {
|
||||||
|
height : auto;
|
||||||
|
&::before {
|
||||||
|
font-size : 1.4em;
|
||||||
|
content : 'ABC';
|
||||||
}
|
}
|
||||||
.name {
|
|
||||||
margin-right : auto;
|
&.OpenSans {font-family : 'OpenSans';}
|
||||||
|
&.CodeBold {font-family : 'CodeBold';}
|
||||||
|
&.CodeLight {font-family : 'CodeLight';}
|
||||||
|
&.ScalySansRemake {font-family : 'ScalySansRemake';}
|
||||||
|
&.BookInsanityRemake {font-family : 'BookInsanityRemake';}
|
||||||
|
&.MrEavesRemake {font-family : 'MrEavesRemake';}
|
||||||
|
&.SolberaImitationRemake {font-family : 'SolberaImitationRemake';}
|
||||||
|
&.ScalySansSmallCapsRemake {font-family : 'ScalySansSmallCapsRemake';}
|
||||||
|
&.WalterTurncoat {font-family : 'WalterTurncoat';}
|
||||||
|
&.Lato {font-family : 'Lato';}
|
||||||
|
&.Courier {font-family : 'Courier';}
|
||||||
|
&.NodestoCapsCondensed {font-family : 'NodestoCapsCondensed';}
|
||||||
|
&.Overpass {font-family : 'Overpass';}
|
||||||
|
&.Davek {font-family : 'Davek';}
|
||||||
|
&.Iokharic {font-family : 'Iokharic';}
|
||||||
|
&.Rellanic {font-family : 'Rellanic';}
|
||||||
|
&.TimesNewRoman {font-family : 'Times New Roman';}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.name { margin-right : auto; }
|
||||||
.beta {
|
.beta {
|
||||||
color : white;
|
|
||||||
padding : 4px 6px;
|
|
||||||
line-height : 1em;
|
|
||||||
margin-left : 5px;
|
|
||||||
align-self : center;
|
align-self : center;
|
||||||
|
padding : 4px 6px;
|
||||||
|
margin-left : 5px;
|
||||||
|
font-family : monospace;
|
||||||
|
line-height : 1em;
|
||||||
|
color : white;
|
||||||
background : grey;
|
background : grey;
|
||||||
border-radius : 12px;
|
border-radius : 12px;
|
||||||
font-family : monospace;
|
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color : #999;
|
background-color : #999999;
|
||||||
& > .dropdown {
|
& > .dropdown {
|
||||||
visibility : visible;
|
visibility : visible;
|
||||||
&.side {
|
&.side {
|
||||||
left: 100%;
|
|
||||||
top : 0%;
|
top : 0%;
|
||||||
|
left : 100%;
|
||||||
margin-left : 0;
|
margin-left : 0;
|
||||||
box-shadow: -1px 1px 2px 0px #999;
|
box-shadow : -1px 1px 2px 0px #999999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const ErrorNavItem = createClass({
|
|||||||
const error = this.props.error;
|
const error = this.props.error;
|
||||||
const response = error.response;
|
const response = error.response;
|
||||||
const status = response.status;
|
const status = response.status;
|
||||||
|
const HBErrorCode = response.body?.HBErrorCode;
|
||||||
const message = response.body?.message;
|
const message = response.body?.message;
|
||||||
let errMsg = '';
|
let errMsg = '';
|
||||||
try {
|
try {
|
||||||
@@ -40,7 +41,9 @@ const ErrorNavItem = createClass({
|
|||||||
{message ?? 'Conflict: please refresh to get latest changes'}
|
{message ?? 'Conflict: please refresh to get latest changes'}
|
||||||
</div>
|
</div>
|
||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
} else if(status === 412) {
|
}
|
||||||
|
|
||||||
|
if(status === 412) {
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
Oops!
|
Oops!
|
||||||
<div className='errorContainer' onClick={clearError}>
|
<div className='errorContainer' onClick={clearError}>
|
||||||
@@ -49,6 +52,36 @@ const ErrorNavItem = createClass({
|
|||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(HBErrorCode === '04') {
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
You are no longer signed in as an author of
|
||||||
|
this brew! Were you signed out from a different
|
||||||
|
window? Visit our log in page, then try again!
|
||||||
|
<br></br>
|
||||||
|
<a target='_blank' rel='noopener noreferrer'
|
||||||
|
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
||||||
|
<div className='confirm'>
|
||||||
|
Sign In
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div className='deny'>
|
||||||
|
Not Now
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(response.body?.errors?.[0].reason == 'storageQuotaExceeded') {
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
Can't save because your Google Drive seems to be full!
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
if(response.req.url.match(/^\/api.*Google.*$/m)){
|
if(response.req.url.match(/^\/api.*Google.*$/m)){
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
Oops!
|
Oops!
|
||||||
@@ -57,6 +90,7 @@ const ErrorNavItem = createClass({
|
|||||||
expired! Visit our log in page to sign out
|
expired! Visit our log in page to sign out
|
||||||
and sign back in with Google,
|
and sign back in with Google,
|
||||||
then try saving again!
|
then try saving again!
|
||||||
|
<br></br>
|
||||||
<a target='_blank' rel='noopener noreferrer'
|
<a target='_blank' rel='noopener noreferrer'
|
||||||
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
||||||
<div className='confirm'>
|
<div className='confirm'>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const Nav = require('naturalcrit/nav/nav.jsx');
|
|||||||
module.exports = function(props){
|
module.exports = function(props){
|
||||||
return <Nav.item
|
return <Nav.item
|
||||||
href='/new'
|
href='/new'
|
||||||
|
newTab={true}
|
||||||
color='purple'
|
color='purple'
|
||||||
icon='fas fa-plus-square'>
|
icon='fas fa-plus-square'>
|
||||||
new
|
new
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ const BrewItem = createClass({
|
|||||||
<div className='info'>
|
<div className='info'>
|
||||||
|
|
||||||
{brew.tags?.length ? <>
|
{brew.tags?.length ? <>
|
||||||
<div className='brewTags' title={`Tags:\n${brew.tags.join('\n')}`}>
|
<div className='brewTags' title={`${brew.tags.length} tags:\n${brew.tags.join('\n')}`}>
|
||||||
<i className='fas fa-tags'/>
|
<i className='fas fa-tags'/>
|
||||||
{brew.tags.map((tag, idx)=>{
|
{brew.tags.map((tag, idx)=>{
|
||||||
const matches = tag.match(/^(?:([^:]+):)?([^:]+)$/);
|
const matches = tag.match(/^(?:([^:]+):)?([^:]+)$/);
|
||||||
@@ -135,7 +135,11 @@ const BrewItem = createClass({
|
|||||||
</> : <></>
|
</> : <></>
|
||||||
}
|
}
|
||||||
<span title={`Authors:\n${brew.authors?.join('\n')}`}>
|
<span title={`Authors:\n${brew.authors?.join('\n')}`}>
|
||||||
<i className='fas fa-user'/> {brew.authors?.join(', ')}
|
<i className='fas fa-user'/> {brew.authors?.map((author, index)=>(
|
||||||
|
<>
|
||||||
|
<a key={index} href={`/user/${author}`}>{author}</a>
|
||||||
|
{index < brew.authors.length - 1 && ', '}
|
||||||
|
</>))}
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
||||||
|
|||||||
@@ -48,6 +48,10 @@
|
|||||||
&>span{
|
&>span{
|
||||||
margin-right : 12px;
|
margin-right : 12px;
|
||||||
line-height : 1.5em;
|
line-height : 1.5em;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color:inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.brewTags span {
|
.brewTags span {
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ const ListPage = createClass({
|
|||||||
render : function(){
|
render : function(){
|
||||||
return <div className='listPage sitePage'>
|
return <div className='listPage sitePage'>
|
||||||
{/*<style>@layer V3_5ePHB, bundle;</style>*/}
|
{/*<style>@layer V3_5ePHB, bundle;</style>*/}
|
||||||
|
<link href='/themes/V3/Blank/style.css' rel='stylesheet'/>
|
||||||
<link href='/themes/V3/5ePHB/style.css' rel='stylesheet'/>
|
<link href='/themes/V3/5ePHB/style.css' rel='stylesheet'/>
|
||||||
{this.props.navItems}
|
{this.props.navItems}
|
||||||
{this.renderSortOptions()}
|
{this.renderSortOptions()}
|
||||||
|
|||||||
@@ -2,17 +2,18 @@
|
|||||||
.noColumns(){
|
.noColumns(){
|
||||||
column-count : auto;
|
column-count : auto;
|
||||||
column-fill : auto;
|
column-fill : auto;
|
||||||
column-gap : auto;
|
column-gap : normal;
|
||||||
column-width : auto;
|
column-width : auto;
|
||||||
-webkit-column-count : auto;
|
-webkit-column-count : auto;
|
||||||
-moz-column-count : auto;
|
-moz-column-count : auto;
|
||||||
-webkit-column-width : auto;
|
-webkit-column-width : auto;
|
||||||
-moz-column-width : auto;
|
-moz-column-width : auto;
|
||||||
-webkit-column-gap : auto;
|
-webkit-column-gap : normal;
|
||||||
-moz-column-gap : auto;
|
-moz-column-gap : normal;
|
||||||
height : auto;
|
height : auto;
|
||||||
min-height : 279.4mm;
|
min-height : 279.4mm;
|
||||||
margin : 20px auto;
|
margin : 20px auto;
|
||||||
|
contain : unset;
|
||||||
}
|
}
|
||||||
.listPage{
|
.listPage{
|
||||||
.content{
|
.content{
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ const EditPage = createClass({
|
|||||||
url : '',
|
url : '',
|
||||||
autoSave : true,
|
autoSave : true,
|
||||||
autoSaveWarning : false,
|
autoSaveWarning : false,
|
||||||
unsavedTime : new Date()
|
unsavedTime : new Date(),
|
||||||
|
currentEditorPage : 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
savedBrew : null,
|
savedBrew : null,
|
||||||
@@ -111,7 +112,8 @@ const EditPage = createClass({
|
|||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, text: text },
|
brew : { ...prevState.brew, text: text },
|
||||||
isPending : true,
|
isPending : true,
|
||||||
htmlErrors : htmlErrors
|
htmlErrors : htmlErrors,
|
||||||
|
currentEditorPage : this.refs.editor.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||||
}), ()=>{if(this.state.autoSave) this.trySave();});
|
}), ()=>{if(this.state.autoSave) this.trySave();});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -405,6 +407,7 @@ const EditPage = createClass({
|
|||||||
theme={this.state.brew.theme}
|
theme={this.state.brew.theme}
|
||||||
errors={this.state.htmlErrors}
|
errors={this.state.htmlErrors}
|
||||||
lang={this.state.brew.lang}
|
lang={this.state.brew.lang}
|
||||||
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
/>
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ const errorIndex = (props)=>{
|
|||||||
|
|
||||||
**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}`;}).join(', ') || 'Unable to list authors'}
|
||||||
|
|
||||||
|
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
||||||
|
|
||||||
// User is not signed in; must be a user on the Authors List
|
// User is not signed in; must be a user on the Authors List
|
||||||
'04' : dedent`
|
'04' : dedent`
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ const HomePage = createClass({
|
|||||||
return {
|
return {
|
||||||
brew : this.props.brew,
|
brew : this.props.brew,
|
||||||
welcomeText : this.props.brew.text,
|
welcomeText : this.props.brew.text,
|
||||||
error : undefined
|
error : undefined,
|
||||||
|
currentEditorPage : 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSave : function(){
|
handleSave : function(){
|
||||||
@@ -53,7 +54,8 @@ const HomePage = createClass({
|
|||||||
},
|
},
|
||||||
handleTextChange : function(text){
|
handleTextChange : function(text){
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, text: text }
|
brew : { ...prevState.brew, text: text },
|
||||||
|
currentEditorPage : this.refs.editor.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
@@ -85,7 +87,12 @@ const HomePage = createClass({
|
|||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
showEditButtons={false}
|
showEditButtons={false}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer}/>
|
<BrewRenderer
|
||||||
|
text={this.state.brew.text}
|
||||||
|
style={this.state.brew.style}
|
||||||
|
renderer={this.state.brew.renderer}
|
||||||
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ const NewPage = createClass({
|
|||||||
isSaving : false,
|
isSaving : false,
|
||||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||||
error : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(brew.text)
|
htmlErrors : Markdown.validate(brew.text),
|
||||||
|
currentEditorPage : 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -105,7 +106,8 @@ const NewPage = createClass({
|
|||||||
|
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, text: text },
|
brew : { ...prevState.brew, text: text },
|
||||||
htmlErrors : htmlErrors
|
htmlErrors : htmlErrors,
|
||||||
|
currentEditorPage : this.refs.editor.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||||
}));
|
}));
|
||||||
localStorage.setItem(BREWKEY, text);
|
localStorage.setItem(BREWKEY, text);
|
||||||
},
|
},
|
||||||
@@ -220,7 +222,15 @@ const NewPage = createClass({
|
|||||||
onMetaChange={this.handleMetaChange}
|
onMetaChange={this.handleMetaChange}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} lang={this.state.brew.lang} errors={this.state.htmlErrors}/>
|
<BrewRenderer
|
||||||
|
text={this.state.brew.text}
|
||||||
|
style={this.state.brew.style}
|
||||||
|
renderer={this.state.brew.renderer}
|
||||||
|
theme={this.state.brew.theme}
|
||||||
|
errors={this.state.htmlErrors}
|
||||||
|
lang={this.state.brew.lang}
|
||||||
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
2
faq.md
2
faq.md
@@ -102,7 +102,7 @@ The best way to avoid this is to leave space at the end of a column equal to one
|
|||||||
|
|
||||||
### Why do I need to manually create a new page? Why doesn't text flow between pages?
|
### Why do I need to manually create a new page? Why doesn't text flow between pages?
|
||||||
|
|
||||||
A Homebrewery document is at it's core an HTML & CSS document, and currently limited by the specs of those technologies. It is currently not possible to flow content from inside one box ("page") to the inside of another box. It seems likely that someday CSS will add this capability, and if/when that happens, Homebrewery will adopt it as soon as possible.
|
A Homebrewery document is at its core an HTML & CSS document, and currently limited by the specs of those technologies. It is currently not possible to flow content from inside one box ("page") to the inside of another box. It seems likely that someday CSS will add this capability, and if/when that happens, Homebrewery will adopt it as soon as possible.
|
||||||
|
|
||||||
### Where do I get images?
|
### Where do I get images?
|
||||||
The Homebrewery does not provide images for use besides some page elements and example images for snippets. You will need to find your own images for use and be sure you are following the appropriate license requirements.
|
The Homebrewery does not provide images for use besides some page elements and example images for snippets. You will need to find your own images for use and be sure you are following the appropriate license requirements.
|
||||||
|
|||||||
1273
package-lock.json
generated
1273
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
44
package.json
44
package.json
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"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.10.0",
|
"version": "3.11.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": "^10.2.x",
|
"npm": "^10.2.x",
|
||||||
"node": ">=20.8.x"
|
"node": "^20.8.x"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"test:coverage": "jest --coverage --silent --runInBand",
|
"test:coverage": "jest --coverage --silent --runInBand",
|
||||||
"test:dev": "jest --verbose --watch",
|
"test:dev": "jest --verbose --watch",
|
||||||
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
||||||
|
"test: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",
|
||||||
@@ -79,53 +80,54 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.23.2",
|
"@babel/core": "^7.23.9",
|
||||||
"@babel/plugin-transform-runtime": "^7.23.2",
|
"@babel/plugin-transform-runtime": "^7.23.9",
|
||||||
"@babel/preset-env": "^7.23.2",
|
"@babel/preset-env": "^7.23.9",
|
||||||
"@babel/preset-react": "^7.22.15",
|
"@babel/preset-react": "^7.23.3",
|
||||||
"@googleapis/drive": "^8.4.0",
|
"@googleapis/drive": "^8.7.0",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"codemirror": "^5.65.6",
|
"codemirror": "^5.65.6",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"create-react-class": "^15.7.0",
|
"create-react-class": "^15.7.0",
|
||||||
"dedent-tabs": "^0.10.3",
|
"dedent-tabs": "^0.10.3",
|
||||||
|
"expr-eval": "^2.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"express-static-gzip": "2.1.7",
|
"express-static-gzip": "2.1.7",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.2.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jwt-simple": "^0.5.6",
|
"jwt-simple": "^0.5.6",
|
||||||
"less": "^3.13.1",
|
"less": "^3.13.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "5.1.1",
|
"marked": "11.2.0",
|
||||||
"marked-extended-tables": "^1.0.7",
|
"marked-extended-tables": "^1.0.8",
|
||||||
"marked-gfm-heading-id": "^3.1.0",
|
"marked-gfm-heading-id": "^3.1.3",
|
||||||
"marked-smartypants-lite": "^1.0.1",
|
"marked-smartypants-lite": "^1.0.2",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.30.1",
|
||||||
"mongoose": "^7.6.1",
|
"mongoose": "^8.1.3",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.0",
|
"nconf": "^0.12.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-frame-component": "^4.1.3",
|
"react-frame-component": "^4.1.3",
|
||||||
"react-router-dom": "6.16.0",
|
"react-router-dom": "6.22.1",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^8.1.2",
|
"superagent": "^8.1.2",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.51.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-jest": "^27.4.2",
|
"eslint-plugin-jest": "^27.6.3",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"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",
|
||||||
"stylelint": "^15.10.3",
|
"stylelint": "^15.11.0",
|
||||||
"stylelint-config-recess-order": "^4.3.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.3"
|
"supertest": "^6.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,10 @@ fs.emptyDirSync('./build');
|
|||||||
|
|
||||||
//v==---------------------------MOVE CM EDITOR THEMES -----------------------------==v//
|
//v==---------------------------MOVE CM EDITOR THEMES -----------------------------==v//
|
||||||
|
|
||||||
editorThemeFiles = fs.readdirSync('./node_modules/codemirror/theme');
|
const editorThemesBuildDir = './build/homebrew/cm-themes';
|
||||||
|
await fs.copy('./node_modules/codemirror/theme', editorThemesBuildDir);
|
||||||
|
await fs.copy('./themes/codeMirror/customThemes', editorThemesBuildDir);
|
||||||
|
editorThemeFiles = fs.readdirSync(editorThemesBuildDir);
|
||||||
|
|
||||||
const editorThemeFile = './themes/codeMirror/editorThemes.json';
|
const editorThemeFile = './themes/codeMirror/editorThemes.json';
|
||||||
if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile);
|
if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile);
|
||||||
@@ -114,7 +117,7 @@ fs.emptyDirSync('./build');
|
|||||||
stream.write('\n]\n');
|
stream.write('\n]\n');
|
||||||
stream.end();
|
stream.end();
|
||||||
|
|
||||||
await fs.copy('./node_modules/codemirror/theme', './build/homebrew/cm-themes');
|
|
||||||
await fs.copy('./themes/codeMirror', './build/homebrew/codeMirror');
|
await fs.copy('./themes/codeMirror', './build/homebrew/codeMirror');
|
||||||
|
|
||||||
//v==----------------------------- BUNDLE PACKAGES --------------------------------==v//
|
//v==----------------------------- BUNDLE PACKAGES --------------------------------==v//
|
||||||
@@ -151,8 +154,6 @@ fs.emptyDirSync('./build');
|
|||||||
// build(bundles);
|
// build(bundles);
|
||||||
//
|
//
|
||||||
|
|
||||||
})().catch(console.error);
|
|
||||||
|
|
||||||
//In development, set up LiveReload (refreshes browser), and Nodemon (restarts server)
|
//In development, set up LiveReload (refreshes browser), and Nodemon (restarts server)
|
||||||
if(isDev){
|
if(isDev){
|
||||||
livereload('./build'); // Install the Chrome extension LiveReload to automatically refresh the browser
|
livereload('./build'); // Install the Chrome extension LiveReload to automatically refresh the browser
|
||||||
@@ -162,3 +163,5 @@ if(isDev){
|
|||||||
//watch : ['./server', './themes'], // Watch additional folders if needed
|
//watch : ['./server', './themes'], // Watch additional folders if needed
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
})().catch(console.error);
|
||||||
@@ -26,7 +26,6 @@
|
|||||||
"codemirror/addon/edit/trailingspace.js",
|
"codemirror/addon/edit/trailingspace.js",
|
||||||
"codemirror/addon/selection/active-line.js",
|
"codemirror/addon/selection/active-line.js",
|
||||||
"moment",
|
"moment",
|
||||||
"superagent",
|
"superagent"
|
||||||
"marked"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,77 +26,116 @@ const mw = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const junkBrewPipeline = [
|
||||||
/* Search for brews that are older than 3 days and that are shorter than a tweet */
|
{ $match : {
|
||||||
const junkBrewQuery = HomebrewModel.find({
|
updatedAt : { $lt: Moment().subtract(30, 'days').toDate() },
|
||||||
'$where' : 'this.text.length < 140',
|
lastViewed : { $lt: Moment().subtract(30, 'days').toDate() }
|
||||||
createdAt : {
|
}},
|
||||||
$lt : Moment().subtract(30, 'days').toDate()
|
{ $project: { textBinSize: { $binarySize: '$textBin' } } },
|
||||||
}
|
{ $match: { textBinSize: { $lt: 140 } } },
|
||||||
}).limit(100).maxTime(60000);
|
{ $limit: 100 }
|
||||||
|
];
|
||||||
|
|
||||||
/* Search for brews that aren't compressed (missing the compressed text field) */
|
/* Search for brews that aren't compressed (missing the compressed text field) */
|
||||||
const uncompressedBrewQuery = HomebrewModel.find({
|
const uncompressedBrewQuery = HomebrewModel.find({
|
||||||
'text' : { '$exists': true }
|
'text' : { '$exists': true }
|
||||||
}).lean().limit(10000).select('_id');
|
}).lean().limit(10000).select('_id');
|
||||||
|
|
||||||
|
// Search for up to 100 brews that have not been viewed or updated in 30 days and are shorter than 140 bytes
|
||||||
router.get('/admin/cleanup', mw.adminOnly, (req, res)=>{
|
router.get('/admin/cleanup', mw.adminOnly, (req, res)=>{
|
||||||
junkBrewQuery.exec((err, objs)=>{
|
HomebrewModel.aggregate(junkBrewPipeline).option({ maxTimeMS: 60000 })
|
||||||
if(err) return res.status(500).send(err);
|
.then((objs)=>res.json({ count: objs.length }))
|
||||||
return res.json({ count: objs.length });
|
.catch((error)=>{
|
||||||
|
console.error(error);
|
||||||
|
res.status(500).json({ error: 'Internal Server Error' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
/* Removes all empty brews that are older than 3 days and that are shorter than a tweet */
|
|
||||||
|
// Delete up to 100 brews that have not been viewed or updated in 30 days and are shorter than 140 bytes
|
||||||
router.post('/admin/cleanup', mw.adminOnly, (req, res)=>{
|
router.post('/admin/cleanup', mw.adminOnly, (req, res)=>{
|
||||||
junkBrewQuery.remove().exec((err, objs)=>{
|
HomebrewModel.aggregate(junkBrewPipeline).option({ maxTimeMS: 60000 })
|
||||||
if(err) return res.status(500).send(err);
|
.then((docs)=>{
|
||||||
return res.json({ count: objs.length });
|
const ids = docs.map((doc)=>doc._id);
|
||||||
|
return HomebrewModel.deleteMany({ _id: { $in: ids } });
|
||||||
|
}).then((result)=>{
|
||||||
|
res.json({ count: result.deletedCount });
|
||||||
|
}).catch((error)=>{
|
||||||
|
console.error(error);
|
||||||
|
res.status(500).json({ error: 'Internal Server Error' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Searches for matching edit or share id, also attempts to partial match */
|
/* Searches for matching edit or share id, also attempts to partial match */
|
||||||
router.get('/admin/lookup/:id', mw.adminOnly, (req, res, next)=>{
|
router.get('/admin/lookup/:id', mw.adminOnly, async (req, res, next)=>{
|
||||||
HomebrewModel.findOne({ $or : [
|
HomebrewModel.findOne({
|
||||||
{ editId: { '$regex': req.params.id, '$options': 'i' } },
|
$or : [
|
||||||
{ shareId: { '$regex': req.params.id, '$options': 'i' } },
|
{ editId: { $regex: req.params.id, $options: 'i' } },
|
||||||
] }).exec((err, brew)=>{
|
{ shareId: { $regex: req.params.id, $options: 'i' } },
|
||||||
|
]
|
||||||
|
}).exec()
|
||||||
|
.then((brew)=>{
|
||||||
|
if(!brew) // No document found
|
||||||
|
return res.status(404).json({ error: 'Document not found' });
|
||||||
|
else
|
||||||
return res.json(brew);
|
return res.json(brew);
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ error: 'Internal Server Error' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Find 50 brews that aren't compressed yet */
|
/* Find 50 brews that aren't compressed yet */
|
||||||
router.get('/admin/finduncompressed', mw.adminOnly, (req, res)=>{
|
router.get('/admin/finduncompressed', mw.adminOnly, (req, res)=>{
|
||||||
uncompressedBrewQuery.exec((err, objs)=>{
|
const query = uncompressedBrewQuery.clone();
|
||||||
if(err) return res.status(500).send(err);
|
|
||||||
objs = objs.map((obj)=>{return obj._id;});
|
query.exec()
|
||||||
return res.json({ count: objs.length, ids: objs });
|
.then((objs)=>{
|
||||||
|
const ids = objs.map((obj)=>obj._id);
|
||||||
|
res.json({ count: ids.length, ids });
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send(err.message || 'Internal Server Error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* Compresses the "text" field of a brew to binary */
|
/* Compresses the "text" field of a brew to binary */
|
||||||
router.put('/admin/compress/:id', (req, res)=>{
|
router.put('/admin/compress/:id', (req, res)=>{
|
||||||
HomebrewModel.get({ _id: req.params.id })
|
HomebrewModel.findOne({ _id: req.params.id })
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
brew.textBin = zlib.deflateRawSync(brew.text); // Compress brew text to binary before saving
|
if(!brew)
|
||||||
brew.text = undefined; // Delete the non-binary text field since it's not needed anymore
|
return res.status(404).send('Brew not found');
|
||||||
|
|
||||||
brew.save((err, obj)=>{
|
if(brew.text) {
|
||||||
if(err) throw err;
|
brew.textBin = brew.textBin || zlib.deflateRawSync(brew.text); //Don't overwrite textBin if exists
|
||||||
return res.status(200).send(obj);
|
brew.text = undefined;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return brew.save();
|
||||||
})
|
})
|
||||||
|
.then((obj)=>res.status(200).send(obj))
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err);
|
console.error(err);
|
||||||
return res.status(500).send('Error while saving');
|
res.status(500).send('Error while saving');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/admin/stats', mw.adminOnly, (req, res)=>{
|
|
||||||
HomebrewModel.count({}, (err, count)=>{
|
router.get('/admin/stats', mw.adminOnly, async (req, res)=>{
|
||||||
|
try {
|
||||||
|
const totalBrewsCount = await HomebrewModel.countDocuments({});
|
||||||
|
const publishedBrewsCount = await HomebrewModel.countDocuments({ published: true });
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
totalBrews : count
|
totalBrews : totalBrewsCount,
|
||||||
});
|
totalPublishedBrews : publishedBrewsCount
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return res.status(500).json({ error: 'Internal Server Error' });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/admin', mw.adminOnly, (req, res)=>{
|
router.get('/admin', mw.adminOnly, (req, res)=>{
|
||||||
|
|||||||
@@ -304,7 +304,8 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
|
|||||||
text : req.brew.text,
|
text : req.brew.text,
|
||||||
style : req.brew.style,
|
style : req.brew.style,
|
||||||
renderer : req.brew.renderer,
|
renderer : req.brew.renderer,
|
||||||
theme : req.brew.theme
|
theme : req.brew.theme,
|
||||||
|
tags : req.brew.tags
|
||||||
};
|
};
|
||||||
req.brew = _.defaults(brew, DEFAULT_BREW);
|
req.brew = _.defaults(brew, DEFAULT_BREW);
|
||||||
|
|
||||||
@@ -327,6 +328,8 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
|
|||||||
type : 'article'
|
type : 'article'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// increase visitor view count, do not include visits by author(s)
|
||||||
|
if(!brew.authors.includes(req.account?.username)){
|
||||||
if(req.params.id.length > 12 && !brew._id) {
|
if(req.params.id.length > 12 && !brew._id) {
|
||||||
const googleId = brew.googleId;
|
const googleId = brew.googleId;
|
||||||
const shareId = brew.shareId;
|
const shareId = brew.shareId;
|
||||||
@@ -335,6 +338,7 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
|
|||||||
} else {
|
} else {
|
||||||
await HomebrewModel.increaseView({ shareId: brew.shareId });
|
await HomebrewModel.increaseView({ shareId: brew.shareId });
|
||||||
}
|
}
|
||||||
|
};
|
||||||
sanitizeBrew(req.brew, 'share');
|
sanitizeBrew(req.brew, 'share');
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
@@ -469,9 +473,18 @@ const getPureError = (error)=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
app.use(async (err, req, res, next)=>{
|
app.use(async (err, req, res, next)=>{
|
||||||
const status = err.status || err.code || 500;
|
err.originalUrl = req.originalUrl;
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
|
if(err.originalUrl?.startsWith('/api/')) {
|
||||||
|
// console.log('API error');
|
||||||
|
res.status(err.status || err.response?.status || 500).send(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('non-API error');
|
||||||
|
const status = err.status || err.code || 500;
|
||||||
|
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
title : 'Error Page',
|
title : 'Error Page',
|
||||||
description : 'Something went wrong!'
|
description : 'Something went wrong!'
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ 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 };
|
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 };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const cx = require('classnames');
|
|||||||
const closeTag = require('./close-tag');
|
const closeTag = require('./close-tag');
|
||||||
|
|
||||||
let CodeMirror;
|
let CodeMirror;
|
||||||
if(typeof navigator !== 'undefined'){
|
if(typeof window !== 'undefined'){
|
||||||
CodeMirror = require('codemirror');
|
CodeMirror = require('codemirror');
|
||||||
|
|
||||||
//Language Modes
|
//Language Modes
|
||||||
@@ -112,6 +112,10 @@ const CodeEditor = createClass({
|
|||||||
'Shift-Tab' : this.dedent,
|
'Shift-Tab' : this.dedent,
|
||||||
'Ctrl-B' : this.makeBold,
|
'Ctrl-B' : this.makeBold,
|
||||||
'Cmd-B' : this.makeBold,
|
'Cmd-B' : this.makeBold,
|
||||||
|
'Shift-Ctrl-=' : this.makeSuper,
|
||||||
|
'Shift-Cmd-=' : this.makeSuper,
|
||||||
|
'Ctrl-=' : this.makeSub,
|
||||||
|
'Cmd-=' : this.makeSub,
|
||||||
'Ctrl-I' : this.makeItalic,
|
'Ctrl-I' : this.makeItalic,
|
||||||
'Cmd-I' : this.makeItalic,
|
'Cmd-I' : this.makeItalic,
|
||||||
'Ctrl-U' : this.makeUnderline,
|
'Ctrl-U' : this.makeUnderline,
|
||||||
@@ -219,6 +223,25 @@ const CodeEditor = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
makeSuper : function() {
|
||||||
|
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '^' && selection.slice(-1) === '^';
|
||||||
|
this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `^${selection}^`, 'around');
|
||||||
|
if(selection.length === 0){
|
||||||
|
const cursor = this.codeMirror.getCursor();
|
||||||
|
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
makeSub : function() {
|
||||||
|
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '^^' && selection.slice(-2) === '^^';
|
||||||
|
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `^^${selection}^^`, 'around');
|
||||||
|
if(selection.length === 0){
|
||||||
|
const cursor = this.codeMirror.getCursor();
|
||||||
|
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
makeNbsp : function() {
|
makeNbsp : function() {
|
||||||
this.codeMirror.replaceSelection(' ', 'end');
|
this.codeMirror.replaceSelection(' ', 'end');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,40 @@ 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 MathParser = require('expr-eval').Parser;
|
||||||
const renderer = new Marked.Renderer();
|
const renderer = new Marked.Renderer();
|
||||||
|
const tokenizer = new Marked.Tokenizer();
|
||||||
|
|
||||||
|
//Limit math features to simple items
|
||||||
|
const mathParser = new MathParser({
|
||||||
|
operators : {
|
||||||
|
// These default to true, but are included to be explicit
|
||||||
|
add : true,
|
||||||
|
subtract : true,
|
||||||
|
multiply : true,
|
||||||
|
divide : true,
|
||||||
|
power : true,
|
||||||
|
round : true,
|
||||||
|
floor : true,
|
||||||
|
ceil : true,
|
||||||
|
|
||||||
|
sin : false, cos : false, tan : false, asin : false, acos : false,
|
||||||
|
atan : false, sinh : false, cosh : false, tanh : false, asinh : false,
|
||||||
|
acosh : false, atanh : false, sqrt : false, cbrt : false, log : false,
|
||||||
|
log2 : false, ln : false, lg : false, log10 : false, expm1 : false,
|
||||||
|
log1p : false, abs : false, trunc : false, join : false, sum : false,
|
||||||
|
'-' : false, '+' : false, exp : false, not : false, length : false,
|
||||||
|
'!' : false, sign : false, random : false, fac : false, min : false,
|
||||||
|
max : false, hypot : false, pyt : false, pow : false, atan2 : false,
|
||||||
|
'if' : false, gamma : false, roundTo : false, map : false, fold : false,
|
||||||
|
filter : false, indexOf : false,
|
||||||
|
|
||||||
|
remainder : false, factorial : false,
|
||||||
|
comparison : false, concatenate : false,
|
||||||
|
logical : false, assignment : false,
|
||||||
|
array : false, fndef : false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//Processes the markdown within an HTML block if it's just a class-wrapper
|
//Processes the markdown within an HTML block if it's just a class-wrapper
|
||||||
renderer.html = function (html) {
|
renderer.html = function (html) {
|
||||||
@@ -28,13 +61,40 @@ renderer.paragraph = function(text){
|
|||||||
return `<p>${text}</p>\n`;
|
return `<p>${text}</p>\n`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Fix local links in the Preview iFrame to link inside the frame
|
||||||
|
renderer.link = function (href, title, text) {
|
||||||
|
let self = false;
|
||||||
|
if(href[0] == '#') {
|
||||||
|
self = true;
|
||||||
|
}
|
||||||
|
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||||
|
|
||||||
|
if(href === null) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
let out = `<a href="${escape(href)}"`;
|
||||||
|
if(title) {
|
||||||
|
out += ` title="${title}"`;
|
||||||
|
}
|
||||||
|
if(self) {
|
||||||
|
out += ' target="_self"';
|
||||||
|
}
|
||||||
|
out += `>${text}</a>`;
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable default reflink behavior, as it steps on our variables extension
|
||||||
|
tokenizer.def = function () {
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const mustacheSpans = {
|
const mustacheSpans = {
|
||||||
name : 'mustacheSpans',
|
name : 'mustacheSpans',
|
||||||
level : 'inline', // Is this a block-level or inline-level tokenizer?
|
level : 'inline', // Is this a block-level or inline-level tokenizer?
|
||||||
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
||||||
const inlineRegex = /{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *|}}/g;
|
const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
|
||||||
const match = completeSpan.exec(src);
|
const match = completeSpan.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
@@ -84,7 +144,7 @@ const mustacheDivs = {
|
|||||||
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
||||||
const blockRegex = /^ *{{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1 *$|^ *}}$/gm;
|
const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
|
||||||
const match = completeBlock.exec(src);
|
const match = completeBlock.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
@@ -96,7 +156,7 @@ const mustacheDivs = {
|
|||||||
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
||||||
if(!tags) {
|
if(!tags) {
|
||||||
tags = `${processStyleTags(delim.substring(2))}`;
|
tags = `${processStyleTags(delim.substring(2))}`;
|
||||||
endTags = delim.length;
|
endTags = delim.length + src.indexOf(delim);
|
||||||
}
|
}
|
||||||
if(delim.startsWith('{{')) {
|
if(delim.startsWith('{{')) {
|
||||||
blockCount++;
|
blockCount++;
|
||||||
@@ -132,7 +192,7 @@ const mustacheInjectInline = {
|
|||||||
level : 'inline',
|
level : 'inline',
|
||||||
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const inlineRegex = /^ *{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1}/g;
|
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
@@ -167,7 +227,7 @@ const mustacheInjectBlock = {
|
|||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const inlineRegex = /^ *{(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\1}/ym;
|
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
@@ -206,6 +266,34 @@ const mustacheInjectBlock = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const superSubScripts = {
|
||||||
|
name : 'superSubScript',
|
||||||
|
level : 'inline',
|
||||||
|
start(src) { return src.match(/\^/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
|
tokenizer(src, tokens) {
|
||||||
|
const superRegex = /^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/m;
|
||||||
|
const subRegex = /^\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/m;
|
||||||
|
let isSuper = false;
|
||||||
|
let match = subRegex.exec(src);
|
||||||
|
if(!match){
|
||||||
|
match = superRegex.exec(src);
|
||||||
|
if(match)
|
||||||
|
isSuper = true;
|
||||||
|
}
|
||||||
|
if(match?.length) {
|
||||||
|
return {
|
||||||
|
type : 'superSubScript', // Should match "name" above
|
||||||
|
raw : match[0], // Text to consume from the source
|
||||||
|
tag : isSuper ? 'sup' : 'sub',
|
||||||
|
tokens : this.lexer.inlineTokens(match[1])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
return `<${token.tag}>${this.parser.parseInline(token.tokens)}</${token.tag}>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const definitionLists = {
|
const definitionLists = {
|
||||||
name : 'definitionLists',
|
name : 'definitionLists',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
@@ -238,33 +326,257 @@ const definitionLists = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists] });
|
|
||||||
Marked.use(mustacheInjectBlock);
|
|
||||||
Marked.use({ renderer: renderer, mangle: false });
|
|
||||||
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
|
||||||
|
|
||||||
//Fix local links in the Preview iFrame to link inside the frame
|
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
||||||
renderer.link = function (href, title, text) {
|
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
||||||
let self = false;
|
const regex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
||||||
if(href[0] == '#') {
|
const match = regex.exec(input);
|
||||||
self = true;
|
|
||||||
}
|
|
||||||
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
|
||||||
|
|
||||||
if(href === null) {
|
const prefix = match[1];
|
||||||
return text;
|
const label = match[2];
|
||||||
|
|
||||||
|
//v=====--------------------< HANDLE MATH >-------------------=====v//
|
||||||
|
const mathRegex = /[a-z]+\(|[+\-*/^()]/g;
|
||||||
|
const matches = label.split(mathRegex);
|
||||||
|
const mathVars = matches.filter((match)=>isNaN(match))?.map((s)=>s.trim()); // Capture any variable names
|
||||||
|
|
||||||
|
let replacedLabel = label;
|
||||||
|
|
||||||
|
if(mathVars?.[0] !== label.trim()) {// If there was mathy stuff not captured, let's do math!
|
||||||
|
mathVars?.forEach((variable)=>{
|
||||||
|
const foundVar = lookupVar(variable, globalPageNumber, hoist);
|
||||||
|
if(foundVar && foundVar.resolved && foundVar.content && !isNaN(foundVar.content)) // Only subsitute math values if fully resolved, not empty strings, and numbers
|
||||||
|
replacedLabel = replacedLabel.replaceAll(variable, foundVar.content);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
return mathParser.evaluate(replacedLabel);
|
||||||
|
} catch (error) {
|
||||||
|
return undefined; // Return undefined if invalid math result
|
||||||
}
|
}
|
||||||
let out = `<a href="${escape(href)}"`;
|
|
||||||
if(title) {
|
|
||||||
out += ` title="${title}"`;
|
|
||||||
}
|
}
|
||||||
if(self) {
|
//^=====--------------------< HANDLE MATH >-------------------=====^//
|
||||||
out += ' target="_self"';
|
|
||||||
}
|
const foundVar = lookupVar(label, globalPageNumber, hoist);
|
||||||
out += `>${text}</a>`;
|
|
||||||
return out;
|
if(!foundVar || (!foundVar.resolved && !allowUnresolved))
|
||||||
|
return undefined; // Return undefined if not found, or parially-resolved vars are not allowed
|
||||||
|
|
||||||
|
// url or <url> "title" or 'title' or (title)
|
||||||
|
const linkRegex = /^([^<\s][^\s]*|<.*?>)(?: ("(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\((?:\\\(|\\\)|[^()])*\)))?$/m;
|
||||||
|
const linkMatch = linkRegex.exec(foundVar.content);
|
||||||
|
|
||||||
|
const href = linkMatch ? linkMatch[1] : null; //TODO: TRIM OFF < > IF PRESENT
|
||||||
|
const title = linkMatch ? linkMatch[2]?.slice(1, -1) : null;
|
||||||
|
|
||||||
|
if(!prefix[0] && href) // Link
|
||||||
|
return `[${label}](${href}${title ? ` "${title}"` : ''})`;
|
||||||
|
|
||||||
|
if(prefix[0] == '!' && href) // Image
|
||||||
|
return ``;
|
||||||
|
|
||||||
|
if(prefix[0] == '$') // Variable
|
||||||
|
return foundVar.content;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lookupVar = function(label, index, hoist=false) {
|
||||||
|
while (index >= 0) {
|
||||||
|
if(globalVarsList[index]?.[label] !== undefined)
|
||||||
|
return globalVarsList[index][label];
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hoist) { //If normal lookup failed, attempt hoisting
|
||||||
|
index = Object.keys(globalVarsList).length; // Move index to start from last page
|
||||||
|
while (index >= 0) {
|
||||||
|
if(globalVarsList[index]?.[label] !== undefined)
|
||||||
|
return globalVarsList[index][label];
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const processVariableQueue = function() {
|
||||||
|
let resolvedOne = true;
|
||||||
|
let finalLoop = false;
|
||||||
|
while (resolvedOne || finalLoop) { // Loop through queue until no more variable calls can be resolved
|
||||||
|
resolvedOne = false;
|
||||||
|
for (const item of varsQueue) {
|
||||||
|
if(item.type == 'text')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(item.type == 'varDefBlock') {
|
||||||
|
const regex = /[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
||||||
|
let match;
|
||||||
|
let resolved = true;
|
||||||
|
let tempContent = item.content;
|
||||||
|
while (match = regex.exec(item.content)) { // regex to find variable calls
|
||||||
|
const value = replaceVar(match[0], true);
|
||||||
|
|
||||||
|
if(value == undefined)
|
||||||
|
resolved = false;
|
||||||
|
else
|
||||||
|
tempContent = tempContent.replaceAll(match[0], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resolved == true || item.content != tempContent) {
|
||||||
|
resolvedOne = true;
|
||||||
|
item.content = tempContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalVarsList[globalPageNumber][item.varName] = {
|
||||||
|
content : item.content,
|
||||||
|
resolved : resolved
|
||||||
|
};
|
||||||
|
|
||||||
|
if(resolved)
|
||||||
|
item.type = 'resolved';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item.type == 'varCallBlock' || item.type == 'varCallInline') {
|
||||||
|
const value = replaceVar(item.content, true, finalLoop); // final loop will just use the best value so far
|
||||||
|
|
||||||
|
if(value == undefined)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
resolvedOne = true;
|
||||||
|
item.content = value;
|
||||||
|
item.type = 'text';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
varsQueue = varsQueue.filter((item)=>item.type !== 'resolved'); // Remove any fully-resolved variable definitions
|
||||||
|
|
||||||
|
if(finalLoop)
|
||||||
|
break;
|
||||||
|
if(!resolvedOne)
|
||||||
|
finalLoop = true;
|
||||||
|
}
|
||||||
|
varsQueue = varsQueue.filter((item)=>item.type !== 'varDefBlock');
|
||||||
|
};
|
||||||
|
|
||||||
|
function MarkedVariables() {
|
||||||
|
return {
|
||||||
|
hooks : {
|
||||||
|
preprocess(src) {
|
||||||
|
const codeBlockSkip = /^(?: {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+|^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})(?:[^\n]*)(?:\n|$)(?:|(?:[\s\S]*?)(?:\n|$))(?: {0,3}\2[~`]* *(?=\n|$))|`[^`]*?`/;
|
||||||
|
const blockDefRegex = /^[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]:(?!\() *((?:\n? *[^\s].*)+)(?=\n+|$)/; //Matches 3, [4]:5
|
||||||
|
const blockCallRegex = /^[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\](?=\n|$)/; //Matches 6, [7]
|
||||||
|
const inlineDefRegex = /([!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\])\(([^\n]+)\)/; //Matches 8, 9[10](11)
|
||||||
|
const inlineCallRegex = /[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\](?!\()/; //Matches 12, [13]
|
||||||
|
|
||||||
|
// Combine regexes and wrap in parens like so: (regex1)|(regex2)|(regex3)|(regex4)
|
||||||
|
const combinedRegex = new RegExp([codeBlockSkip, blockDefRegex, blockCallRegex, inlineDefRegex, inlineCallRegex].map((s)=>`(${s.source})`).join('|'), 'gm');
|
||||||
|
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
while ((match = combinedRegex.exec(src)) !== null) {
|
||||||
|
// Format any matches into tokens and store
|
||||||
|
if(match.index > lastIndex) { // Any non-variable stuff
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'text',
|
||||||
|
varName : null,
|
||||||
|
content : src.slice(lastIndex, match.index)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[1]) {
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'text',
|
||||||
|
varName : null,
|
||||||
|
content : match[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[3]) { // Block Definition
|
||||||
|
const label = match[4] ? match[4].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varDefBlock',
|
||||||
|
varName : label,
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[6]) { // Block Call
|
||||||
|
const label = match[7] ? match[7].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varCallBlock',
|
||||||
|
varName : label,
|
||||||
|
content : match[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[8]) { // Inline Definition
|
||||||
|
const label = match[10] ? match[10].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
let content = match[11] ? match[11].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
// In case of nested (), find the correct matching end )
|
||||||
|
let level = 0;
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < content.length; i++) {
|
||||||
|
if(content[i] === '\\') {
|
||||||
|
i++;
|
||||||
|
} else if(content[i] === '(') {
|
||||||
|
level++;
|
||||||
|
} else if(content[i] === ')') {
|
||||||
|
level--;
|
||||||
|
if(level < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i > -1) {
|
||||||
|
combinedRegex.lastIndex = combinedRegex.lastIndex - (content.length - i);
|
||||||
|
content = content.slice(0, i).trim().replace(/\s+/g, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varDefBlock',
|
||||||
|
varName : label,
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varCallInline',
|
||||||
|
varName : label,
|
||||||
|
content : match[9]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[12]) { // Inline Call
|
||||||
|
const label = match[13] ? match[13].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varCallInline',
|
||||||
|
varName : label,
|
||||||
|
content : match[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
lastIndex = combinedRegex.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastIndex < src.length) {
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'text',
|
||||||
|
varName : null,
|
||||||
|
content : src.slice(lastIndex)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processVariableQueue();
|
||||||
|
|
||||||
|
const output = varsQueue.map((item)=>item.content).join('');
|
||||||
|
varsQueue = []; // Must clear varsQueue because custom HTML renderer uses Marked.parse which will preprocess again without clearing the array
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//^=====--------------------< Variable Handling >-------------------=====^//
|
||||||
|
|
||||||
|
Marked.use(MarkedVariables());
|
||||||
|
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] });
|
||||||
|
Marked.use(mustacheInjectBlock);
|
||||||
|
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
||||||
|
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
||||||
|
|
||||||
const nonWordAndColonTest = /[^\w:]/g;
|
const nonWordAndColonTest = /[^\w:]/g;
|
||||||
const cleanUrl = function (sanitize, base, href) {
|
const cleanUrl = function (sanitize, base, href) {
|
||||||
if(sanitize) {
|
if(sanitize) {
|
||||||
@@ -326,24 +638,43 @@ const voidTags = new Set([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const processStyleTags = (string)=>{
|
const processStyleTags = (string)=>{
|
||||||
//split tags up. quotes can only occur right after colons.
|
//split tags up. quotes can only occur right after : or =.
|
||||||
//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);
|
||||||
|
|
||||||
if(!tags) return '"';
|
|
||||||
|
|
||||||
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];
|
||||||
const classes = _.remove(tags, (tag)=>!tag.includes(':'));
|
const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('=')));
|
||||||
const styles = tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;'));
|
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'));
|
||||||
return `${classes.join(' ')}" ${id ? `id="${id}"` : ''} ${styles.length ? `style="${styles.join(' ')}"` : ''}`;
|
const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()) : [];
|
||||||
|
|
||||||
|
return `${classes?.length ? ` ${classes.join(' ')}` : ''}"` +
|
||||||
|
`${id ? ` id="${id}"` : ''}` +
|
||||||
|
`${styles?.length ? ` style="${styles.join(' ')}"` : ''}` +
|
||||||
|
`${attributes?.length ? ` ${attributes.join(' ')}` : ''}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const globalVarsList = {};
|
||||||
|
let varsQueue = [];
|
||||||
|
let globalPageNumber = 0;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
marked : Marked,
|
marked : Marked,
|
||||||
render : (rawBrewText)=>{
|
render : (rawBrewText, pageNumber=1)=>{
|
||||||
|
globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
|
||||||
|
varsQueue = []; //Could move into MarkedVariables()
|
||||||
|
globalPageNumber = pageNumber;
|
||||||
|
|
||||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
||||||
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
||||||
return Marked.parse(rawBrewText);
|
const opts = Marked.defaults;
|
||||||
|
|
||||||
|
rawBrewText = opts.hooks.preprocess(rawBrewText);
|
||||||
|
const tokens = Marked.lexer(rawBrewText, opts);
|
||||||
|
|
||||||
|
Marked.walkTokens(tokens, opts.walkTokens);
|
||||||
|
|
||||||
|
const html = Marked.parser(tokens, opts);
|
||||||
|
return opts.hooks.postprocess(html);
|
||||||
},
|
},
|
||||||
|
|
||||||
validate : (rawBrewText)=>{
|
validate : (rawBrewText)=>{
|
||||||
|
|||||||
@@ -13,137 +13,134 @@ String.prototype.trimReturns = function(){
|
|||||||
// Remove the `.failing()` method once you have fixed the issue.
|
// Remove the `.failing()` method once you have fixed the issue.
|
||||||
|
|
||||||
describe('Inline: When using the Inline syntax {{ }}', ()=>{
|
describe('Inline: When using the Inline syntax {{ }}', ()=>{
|
||||||
it.failing('Renders a mustache span with text only', function() {
|
it('Renders a mustache span with text only', function() {
|
||||||
const source = '{{ text}}';
|
const source = '{{ text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text only, but with spaces', function() {
|
it('Renders a mustache span with text only, but with spaces', function() {
|
||||||
const source = '{{ this is a text}}';
|
const source = '{{ this is a text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">this is a text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">this is a text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders an empty mustache span', function() {
|
it('Renders an empty mustache span', function() {
|
||||||
const source = '{{}}';
|
const source = '{{}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with just a space', function() {
|
it('Renders a mustache span with just a space', function() {
|
||||||
const source = '{{ }}';
|
const source = '{{ }}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with a few spaces only', function() {
|
it('Renders a mustache span with a few spaces only', function() {
|
||||||
const source = '{{ }}';
|
const source = '{{ }}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block"></span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and class', function() {
|
it('Renders a mustache span with text and class', function() {
|
||||||
const source = '{{my-class text}}';
|
const source = '{{my-class text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag.
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and two classes', function() {
|
it('Renders a mustache span with text and two classes', function() {
|
||||||
const source = '{{my-class,my-class2 text}}';
|
const source = '{{my-class,my-class2 text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag.
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class my-class2">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class my-class2">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text with spaces and class', function() {
|
it('Renders a mustache span with text with spaces and class', function() {
|
||||||
const source = '{{my-class this is a text}}';
|
const source = '{{my-class this is a text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class">this is a text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block my-class">this is a text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and id', function() {
|
it('Renders a mustache span with text and id', function() {
|
||||||
const source = '{{#my-span text}}';
|
const source = '{{#my-span text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s before closing `>` in opening tag, and another after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="my-span">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="my-span">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and two ids', function() {
|
it('Renders a mustache span with text and two ids', function() {
|
||||||
const source = '{{#my-span,#my-favorite-span text}}';
|
const source = '{{#my-span,#my-favorite-span text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s before closing `>` in opening tag, and another after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="my-span">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="my-span">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and css property', function() {
|
it('Renders a mustache span with text and css property', function() {
|
||||||
const source = '{{color:red text}}';
|
const source = '{{color:red text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" 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 mustache span with text and two css properties', function() {
|
it('Renders a mustache span with text and two css properties', function() {
|
||||||
const source = '{{color:red,padding:5px text}}';
|
const source = '{{color:red,padding:5px text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red; padding:5px;">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red; padding:5px;">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and css property which contains quotes', function() {
|
it('Renders a mustache span with text and css property which contains quotes', function() {
|
||||||
const source = '{{font-family:"trebuchet ms" text}}';
|
const source = '{{font-family:"trebuchet ms" text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms;">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms;">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text and two css properties which contains quotes', function() {
|
it('Renders a mustache span with text and two css properties which contains quotes', function() {
|
||||||
const source = '{{font-family:"trebuchet ms",padding:"5px 10px" text}}';
|
const source = '{{font-family:"trebuchet ms",padding:"5px 10px" text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms; padding:5px 10px;">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms; padding:5px 10px;">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it.failing('Renders a mustache span with text with quotes and css property which contains quotes', function() {
|
it('Renders a mustache span with text with quotes and css property which contains double quotes', function() {
|
||||||
const source = '{{font-family:"trebuchet ms" text "with quotes"}}';
|
const source = '{{font-family:"trebuchet ms" text "with quotes"}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms;">text “with quotes”</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="font-family:trebuchet ms;">text “with quotes”</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('Renders a mustache span with text with quotes and css property which contains double and simple quotes', function() {
|
||||||
|
const source = `{{--stringVariable:"'string'" text "with quotes"}}`;
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<span class="inline-block" style="--stringVariable:'string';">text “with quotes”</span>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('Renders a mustache span with text, id, class and a couple of css properties', function() {
|
it('Renders a mustache span with text, id, class and a couple of css properties', function() {
|
||||||
const source = '{{pen,#author,color:orange,font-family:"trebuchet ms" text}}';
|
const source = '{{pen,#author,color:orange,font-family:"trebuchet ms" text}}';
|
||||||
const rendered = Markdown.render(source);
|
const rendered = Markdown.render(source);
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block pen" id="author" style="color:orange; font-family:trebuchet ms;">text</span>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block pen" id="author" style="color:orange; font-family:trebuchet ms;">text</span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Renders a span with added attributes', function() {
|
||||||
|
const source = 'Text and {{pen,#author,color:orange,font-family:"trebuchet ms",a="b and c",d=e, text}} and more text!';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Text and <span class="inline-block pen" id="author" style="color:orange; font-family:trebuchet ms;" a="b and c" d="e">text</span> and more text!</p>\n');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// BLOCK SYNTAX
|
// BLOCK SYNTAX
|
||||||
|
|
||||||
describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
||||||
it.failing('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();
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
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 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();
|
||||||
// FIXME: adds extra \s after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -151,52 +148,62 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
|||||||
const source = dedent`{{
|
const source = dedent`{{
|
||||||
}}`;
|
}}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
// this actually renders in HB as '{{ }}'...
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>{{}}</p>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>{{}}</p>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('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();
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag
|
|
||||||
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.failing('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();
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag
|
|
||||||
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.failing('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();
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag
|
|
||||||
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.failing('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();
|
||||||
// FIXME: adds two extra \s before closing `>` in opening tag
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red;"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.failing('Renders a div with a class, style and text', function() {
|
it('Renders a div with a style that has a string variable, and text', function() {
|
||||||
|
const source = dedent`{{--stringVariable:"'string'"
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
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>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a div with a style that has a string variable, and text', function() {
|
||||||
|
const source = dedent`{{--stringVariable:"'string'"
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
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>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
// FIXME: adds extra \s after the class attribute
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" style="color:red;"><p>Sample text.</p></div>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" style="color:red;"><p>Sample text.</p></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -208,14 +215,27 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
|
|||||||
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.failing('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();
|
||||||
// FIXME: adds extra \s before closing `>` in opening tag, and another after class names
|
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" id="cat"><p>Sample text.</p></div>`);
|
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() {
|
||||||
|
const source = dedent`{{color:red,cat,#dog,a="b and c",d="e"
|
||||||
|
Sample text.
|
||||||
|
}}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class=\"block cat\" id=\"dog\" style=\"color:red;\" a=\"b and c\" d=\"e\"><p>Sample text.</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a div with added attributes', function() {
|
||||||
|
const source = '{{pen,#author,color:orange,font-family:"trebuchet ms",a="b and c",d=e\nText and text and more text!\n}}\n';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block pen" id="author" style="color:orange; font-family:trebuchet ms;" a="b and c" d="e"><p>Text and text and more text!</p>\n</div>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// MUSTACHE INJECTION SYNTAX
|
// MUSTACHE INJECTION SYNTAX
|
||||||
@@ -235,12 +255,24 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
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() {
|
||||||
|
const source = '{{ text}}{a="b and c"}';
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span a="b and c" class="inline-block ">text</span>');
|
||||||
|
});
|
||||||
|
|
||||||
it.failing('Renders a span "text" with injected style', function() {
|
it.failing('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() {
|
||||||
|
const source = `{{ text}}{--stringVariable:"'string'"}`;
|
||||||
|
const rendered = Markdown.render(source);
|
||||||
|
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.failing('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);
|
||||||
@@ -270,6 +302,12 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
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>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 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>`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('and that element is a block', ()=>{
|
describe('and that element is a block', ()=>{
|
||||||
@@ -297,7 +335,16 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
}}
|
}}
|
||||||
{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() {
|
||||||
|
const source = dedent`{{
|
||||||
|
text
|
||||||
|
}}
|
||||||
|
{--stringVariable:"'string'"}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
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.failing('renders an h2 header "text" with injected class name', function() {
|
||||||
|
|||||||
373
tests/markdown/variables.test.js
Normal file
373
tests/markdown/variables.test.js
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
|
// Marked.js adds line returns after closing tags on some default tokens.
|
||||||
|
// This removes those line returns for comparison sake.
|
||||||
|
String.prototype.trimReturns = function(){
|
||||||
|
return this.replace(/\r?\n|\r/g, '').trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
renderAllPages = function(pages){
|
||||||
|
const outputs = [];
|
||||||
|
pages.forEach((page, index)=>{
|
||||||
|
const output = Markdown.render(page, index);
|
||||||
|
outputs.push(output);
|
||||||
|
});
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding `.failing()` method to `describe` or `it` will make failing tests "pass" as long as they continue to fail.
|
||||||
|
// Remove the `.failing()` method once you have fixed the issue.
|
||||||
|
|
||||||
|
describe('Block-level variables', ()=>{
|
||||||
|
it('Handles variable assignment and recall with simple text', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable assignment and recall with multiline string', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]: string
|
||||||
|
across multiple
|
||||||
|
lines
|
||||||
|
|
||||||
|
$[var]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string across multiple lines</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable assignment and recall with tables', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]:
|
||||||
|
##### Title
|
||||||
|
| H1 | H2 |
|
||||||
|
|:---|:--:|
|
||||||
|
| A | B |
|
||||||
|
| C | D |
|
||||||
|
|
||||||
|
$[var]`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<h5 id="title">Title</h5>
|
||||||
|
<table><thead><tr><th align=left>H1</th>
|
||||||
|
<th align=center>H2</th>
|
||||||
|
</tr></thead><tbody><tr><td align=left>A</td>
|
||||||
|
<td align=center>B</td>
|
||||||
|
</tr><tr><td align=left>C</td>
|
||||||
|
<td align=center>D</td>
|
||||||
|
</tr></tbody></table>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists undefined variables', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: string`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists last instance of variable', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
[var]: new string`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>new string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles complex hoisting', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[titleAndName]: $[title] $[fullName]
|
||||||
|
|
||||||
|
$[title]: Mr.
|
||||||
|
|
||||||
|
$[fullName]: $[firstName] $[lastName]
|
||||||
|
|
||||||
|
[firstName]: Bob
|
||||||
|
|
||||||
|
Welcome, $[titleAndName]!
|
||||||
|
|
||||||
|
[lastName]: Jacob
|
||||||
|
|
||||||
|
[lastName]: $[lastName]son
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Welcome, Mr. Bob Jacobson!</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable reassignment', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]: one
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: two
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>one</p><p>two</p>'.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable reassignment with hoisting', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: one
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: two
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>two</p><p>one</p><p>two</p>'.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ignores undefined variables that can\'t be hoisted', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](My name is $[first] $[last])
|
||||||
|
|
||||||
|
$[last]: Jones
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>My name is $[first] Jones</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inline-level variables', ()=>{
|
||||||
|
it('Handles variable assignment and recall with simple text', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](string)
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string</p><p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists undefined variables when possible', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](My name is $[name] Jones)
|
||||||
|
|
||||||
|
[name]: Bob`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>My name is Bob Jones</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists last instance of variable', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](My name is $[name] Jones)
|
||||||
|
|
||||||
|
$[name](Bob)
|
||||||
|
|
||||||
|
[name]: Bill`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>My name is Bill Jones</p> <p>Bob</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Only captures nested parens if balanced', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var1](A variable (with nested parens) inside)
|
||||||
|
|
||||||
|
$[var1]
|
||||||
|
|
||||||
|
$[var2](A variable ) with unbalanced parens)
|
||||||
|
|
||||||
|
$[var2]`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>A variable (with nested parens) inside</p>
|
||||||
|
<p>A variable (with nested parens) inside</p>
|
||||||
|
<p>A variable with unbalanced parens)</p>
|
||||||
|
<p>A variable</p>
|
||||||
|
`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Math', ()=>{
|
||||||
|
it('Handles simple math using numbers only', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[1 + 3 * 5 - (1 / 4)]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>15.75</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles round function', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[round(1/4)]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>0</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles floor function', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[floor(0.6)]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>0</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles ceil function', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[ceil(0.2)]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>1</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles nested functions', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[ceil(floor(round(0.6)))]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>1</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles simple math with variables', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[num1]: 5
|
||||||
|
|
||||||
|
$[num2]: 4
|
||||||
|
|
||||||
|
Answer is $[answer]($[1 + 3 * num1 - (1 / num2)]).
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Answer is 15.75.</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable incrementing', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[num1]: 5
|
||||||
|
|
||||||
|
Increment num1 to get $[num1]($[num1 + 1]) and again to $[num1]($[num1 + 1]).
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Increment num1 to get 6 and again to 7.</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Code blocks', ()=>{
|
||||||
|
it('Ignores all variables in fenced code blocks', function() {
|
||||||
|
const source = dedent`
|
||||||
|
\`\`\`
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
\`\`\`
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<pre><code>
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
</code></pre>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ignores all variables in indented code blocks', function() {
|
||||||
|
const source = dedent`
|
||||||
|
test
|
||||||
|
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>test</p>
|
||||||
|
|
||||||
|
<pre><code>
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
</code></pre>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ignores all variables in inline code blocks', function() {
|
||||||
|
const source = '[var](Hello) `[link](url)`. This `[var] does not work`';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p><a href="Hello">var</a> <code>[link](url)</code>. This <code>[var] does not work</code></p>`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Normal Links and Images', ()=>{
|
||||||
|
it('Renders normal images', function() {
|
||||||
|
const source = ``;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p><img src="url" alt="alt text"></p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders normal images with a title', function() {
|
||||||
|
const source = 'An image !';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>An image <img src="url" alt="alt text" title="and title">!</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Applies curly injectors to images', function() {
|
||||||
|
const source = `{width:100px}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p><img class="" style="width:100px;" src="url" alt="alt text"></p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders normal links', function() {
|
||||||
|
const source = 'A Link to my [website](url)!';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>A Link to my <a href="url">website</a>!</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders normal links with a title', function() {
|
||||||
|
const source = 'A Link to my [website](url "and title")!';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>A Link to my <a href="url" title="and title">website</a>!</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Cross-page variables', ()=>{
|
||||||
|
it('Handles variable assignment and recall across pages', function() {
|
||||||
|
const source0 = `[var]: string`;
|
||||||
|
const source1 = `$[var]`;
|
||||||
|
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('\\page<p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles hoisting across pages', function() {
|
||||||
|
const source0 = `$[var]`;
|
||||||
|
const source1 = `[var]: string`;
|
||||||
|
renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up
|
||||||
|
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>string</p>\\page');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles reassignment and hoisting across pages', function() {
|
||||||
|
const source0 = `$[var]\n\n[var]: one\n\n$[var]`;
|
||||||
|
const source1 = `[var]: two\n\n$[var]`;
|
||||||
|
renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up
|
||||||
|
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>two</p><p>one</p>\\page<p>two</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -40,7 +40,7 @@ body {
|
|||||||
-webkit-column-gap : 1cm;
|
-webkit-column-gap : 1cm;
|
||||||
-moz-column-gap : 1cm;
|
-moz-column-gap : 1cm;
|
||||||
}
|
}
|
||||||
.phb{
|
.phb, .page{
|
||||||
.useColumns();
|
.useColumns();
|
||||||
counter-increment : phb-page-numbers;
|
counter-increment : phb-page-numbers;
|
||||||
position : relative;
|
position : relative;
|
||||||
@@ -59,6 +59,9 @@ body {
|
|||||||
page-break-before : always;
|
page-break-before : always;
|
||||||
page-break-after : always;
|
page-break-after : always;
|
||||||
contain : size;
|
contain : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phb{
|
||||||
//*****************************
|
//*****************************
|
||||||
// * BASE
|
// * BASE
|
||||||
// *****************************/
|
// *****************************/
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ module.exports = function(classname){
|
|||||||
|
|
||||||
#### Equipment
|
#### Equipment
|
||||||
You start with the following equipment, in addition to the equipment granted by your background:
|
You start with the following equipment, in addition to the equipment granted by your background:
|
||||||
- *(a)* a martial weapon and a shield or *(b)* two martial weapons
|
- (*a*) a martial weapon and a shield or (*b*) two martial weapons
|
||||||
- *(a)* five javelins or *(b)* any simple melee weapon
|
- (*a*) five javelins or (*b*) any simple melee weapon
|
||||||
- ${_.sample(['10 lint fluffs', '1 button', 'a cherished lost sock'])}
|
- ${_.sample(['10 lint fluffs', '1 button', 'a cherished lost sock'])}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@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/icon fonts/font-icons.less';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
//Colors
|
//Colors
|
||||||
@@ -14,11 +15,8 @@
|
|||||||
--HB_Color_Footnotes : #C9AD6A; // Gold
|
--HB_Color_Footnotes : #C9AD6A; // Gold
|
||||||
}
|
}
|
||||||
|
|
||||||
@page { margin : 0; }
|
|
||||||
body { counter-reset : phb-page-numbers; }
|
|
||||||
* { -webkit-print-color-adjust : exact; }
|
|
||||||
.useSansSerif() {
|
.useSansSerif() {
|
||||||
font-family : "ScalySansRemake";
|
font-family : 'ScalySansRemake';
|
||||||
font-size : 0.318cm;
|
font-size : 0.318cm;
|
||||||
line-height : 1.2em;
|
line-height : 1.2em;
|
||||||
p,dl,ul,ol { line-height : 1.2em; }
|
p,dl,ul,ol { line-height : 1.2em; }
|
||||||
@@ -42,29 +40,11 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
-webkit-column-gap : 0.9cm;
|
-webkit-column-gap : 0.9cm;
|
||||||
-moz-column-gap : 0.9cm;
|
-moz-column-gap : 0.9cm;
|
||||||
}
|
}
|
||||||
.columnWrapper {
|
|
||||||
column-gap : inherit;
|
|
||||||
max-height : 100%;
|
|
||||||
column-span : all;
|
|
||||||
columns : inherit;
|
|
||||||
}
|
|
||||||
.page {
|
.page {
|
||||||
.useColumns();
|
.useColumns();
|
||||||
position : relative;
|
font-family : 'BookInsanityRemake';
|
||||||
z-index : 15;
|
|
||||||
box-sizing : border-box;
|
|
||||||
width : 215.9mm;
|
|
||||||
height : 279.4mm;
|
|
||||||
padding : 1.4cm 1.9cm 1.7cm;
|
|
||||||
overflow : hidden;
|
|
||||||
font-family : "BookInsanityRemake";
|
|
||||||
font-size : 0.34cm;
|
font-size : 0.34cm;
|
||||||
counter-increment : phb-page-numbers;
|
|
||||||
background-color : var(--HB_Color_Background);
|
|
||||||
background-image : @backgroundImage;
|
background-image : @backgroundImage;
|
||||||
text-rendering : optimizeLegibility;
|
|
||||||
page-break-before : always;
|
|
||||||
page-break-after : always;
|
|
||||||
}
|
}
|
||||||
// *****************************
|
// *****************************
|
||||||
// * BASE
|
// * BASE
|
||||||
@@ -72,25 +52,19 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
|
|
||||||
.page {
|
.page {
|
||||||
p {
|
p {
|
||||||
display : block;
|
|
||||||
line-height : 1.25em;
|
line-height : 1.25em;
|
||||||
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
|
& + * { margin-top : 0.325cm; } //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
|
||||||
& + * { margin-top : 0.325cm; }
|
|
||||||
& + p { margin-top : 0; }
|
& + p { margin-top : 0; }
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
padding-left : 1.4em;
|
padding-left : 1.4em;
|
||||||
margin-bottom : 0.8em;
|
margin-bottom : 0.8em;
|
||||||
line-height : 1.25em;
|
line-height : 1.25em;
|
||||||
list-style-position : outside;
|
|
||||||
list-style-type : disc;
|
|
||||||
}
|
}
|
||||||
ol {
|
ol {
|
||||||
padding-left : 1.4em;
|
padding-left : 1.4em;
|
||||||
margin-bottom : 0.8em;
|
margin-bottom : 0.8em;
|
||||||
line-height : 1.25em;
|
line-height : 1.25em;
|
||||||
list-style-position : outside;
|
|
||||||
list-style-type : decimal;
|
|
||||||
}
|
}
|
||||||
//Indents after p or lists
|
//Indents after p or lists
|
||||||
p + p, ul + p, ol + p { text-indent : 1em; }
|
p + p, ul + p, ol + p { text-indent : 1em; }
|
||||||
@@ -99,23 +73,11 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
font-weight : bold;
|
font-weight : bold;
|
||||||
letter-spacing : -0.02em;
|
letter-spacing : -0.02em;
|
||||||
}
|
}
|
||||||
em { font-style : italic; }
|
|
||||||
sup {
|
|
||||||
font-size : smaller;
|
|
||||||
line-height : 0;
|
|
||||||
vertical-align : super;
|
|
||||||
}
|
|
||||||
sub {
|
|
||||||
font-size : smaller;
|
|
||||||
line-height : 0;
|
|
||||||
vertical-align : sub;
|
|
||||||
}
|
|
||||||
// *****************************
|
// *****************************
|
||||||
// * HEADERS
|
// * HEADERS
|
||||||
// *****************************/
|
// *****************************/
|
||||||
h1,h2,h3,h4 {
|
h1,h2,h3,h4 {
|
||||||
font-family : "MrEavesRemake";
|
font-family : 'MrEavesRemake';
|
||||||
font-weight : 800;
|
|
||||||
color : var(--HB_Color_HeaderText);
|
color : var(--HB_Color_HeaderText);
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
@@ -132,7 +94,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
margin-top : -0.3cm;
|
margin-top : -0.3cm;
|
||||||
margin-bottom : -20px;
|
margin-bottom : -20px;
|
||||||
margin-left : -40px;
|
margin-left : -40px;
|
||||||
font-family : "SolberaImitationRemake";
|
font-family : 'SolberaImitationRemake';
|
||||||
font-size : 3.5cm;
|
font-size : 3.5cm;
|
||||||
line-height : 1em;
|
line-height : 1em;
|
||||||
color : rgba(0, 0, 0, 0);
|
color : rgba(0, 0, 0, 0);
|
||||||
@@ -172,9 +134,8 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
h5 {
|
h5 {
|
||||||
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly
|
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly
|
||||||
//margin-bottom : 0.02cm;
|
//margin-bottom : 0.02cm;
|
||||||
font-family : "ScalySansSmallCapsRemake";
|
font-family : 'ScalySansSmallCapsRemake';
|
||||||
font-size : 0.423cm;
|
font-size : 0.423cm;
|
||||||
font-weight : 900;
|
|
||||||
line-height : 0.951em; //Font is misaligned. Shift up slightly
|
line-height : 0.951em; //Font is misaligned. Shift up slightly
|
||||||
& + * { margin-top : 0.2cm; }
|
& + * { margin-top : 0.2cm; }
|
||||||
}
|
}
|
||||||
@@ -183,7 +144,6 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
// *****************************/
|
// *****************************/
|
||||||
table {
|
table {
|
||||||
.useSansSerif();
|
.useSansSerif();
|
||||||
width : 100%;
|
|
||||||
line-height : 16px;
|
line-height : 16px;
|
||||||
& + * { margin-top : 0.325cm; }
|
& + * { margin-top : 0.325cm; }
|
||||||
thead {
|
thead {
|
||||||
@@ -241,8 +201,6 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// *****************************
|
// *****************************
|
||||||
// * NOTE
|
// * NOTE
|
||||||
// *****************************/
|
// *****************************/
|
||||||
@@ -299,7 +257,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
.artist {
|
.artist {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
width : auto;
|
width : auto;
|
||||||
font-family : "WalterTurncoat";
|
font-family : 'WalterTurncoat';
|
||||||
font-size : 0.27cm;
|
font-size : 0.27cm;
|
||||||
color : var(--HB_Color_CaptionText);
|
color : var(--HB_Color_CaptionText);
|
||||||
text-align : center;
|
text-align : center;
|
||||||
@@ -309,7 +267,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
text-indent : unset;
|
text-indent : unset;
|
||||||
}
|
}
|
||||||
h5 {
|
h5 {
|
||||||
font-family : "WalterTurncoat";
|
font-family : 'WalterTurncoat';
|
||||||
font-size : 1.3em;
|
font-size : 1.3em;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
@@ -320,41 +278,9 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Watermark */
|
/* Watermark */
|
||||||
.watermark {
|
.watermark { color : black; }
|
||||||
position : absolute;
|
|
||||||
top : 0;
|
|
||||||
left : 0;
|
|
||||||
z-index : 500;
|
|
||||||
display : grid !important;
|
|
||||||
place-items : center;
|
|
||||||
justify-content : center;
|
|
||||||
width : 100%;
|
|
||||||
height : 100%;
|
|
||||||
font-size : 120px;
|
|
||||||
color : black;
|
|
||||||
text-transform : uppercase;
|
|
||||||
mix-blend-mode : overlay;
|
|
||||||
opacity : 30%;
|
|
||||||
transform : rotate(-45deg);
|
|
||||||
p { margin-bottom : none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Watercolor */
|
/* Watercolor */
|
||||||
[class*='watercolor'] {
|
|
||||||
position : absolute;
|
|
||||||
z-index : -2;
|
|
||||||
width : 2000px; /* dimensions need to be real big so the user can set */
|
|
||||||
height : 2000px; /* height or width and the image will maintain aspect ratio */
|
|
||||||
background-color : var(--HB_Color_WatercolorStain); /* default color */
|
|
||||||
background-size : cover;
|
|
||||||
-webkit-mask-image : var(--wc);
|
|
||||||
-webkit-mask-size : contain;
|
|
||||||
-webkit-mask-repeat : no-repeat;
|
|
||||||
mask-image : var(--wc);
|
|
||||||
mask-size : contain;
|
|
||||||
mask-repeat : no-repeat;
|
|
||||||
--wc : @watercolor1; /* default image */
|
|
||||||
}
|
|
||||||
|
|
||||||
.watercolor1 { --wc : @watercolor1; }
|
.watercolor1 { --wc : @watercolor1; }
|
||||||
.watercolor2 { --wc : @watercolor2; }
|
.watercolor2 { --wc : @watercolor2; }
|
||||||
@@ -405,12 +331,9 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
// margin-top : 0.05cm; //Font is misaligned. Shift up slightly
|
font-family : 'ScalySansSmallCapsRemake';
|
||||||
padding-bottom : 0.05cm;
|
font-size : 0.45cm;
|
||||||
font-family : "ScalySansRemake";
|
border-bottom : 1.5px solid var(--HB_Color_HeaderText);
|
||||||
font-weight : 800;
|
|
||||||
font-variant : small-caps;
|
|
||||||
border-bottom : 2px solid var(--HB_Color_HeaderText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Triangle dividers
|
//Triangle dividers
|
||||||
@@ -446,6 +369,8 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
tr { background-color : transparent; }
|
tr { background-color : transparent; }
|
||||||
td,th { padding : 0px; }
|
td,th { padding : 0px; }
|
||||||
}
|
}
|
||||||
|
//indent fix after bulleted lists
|
||||||
|
:is(ul,ol) + p { text-indent : 0; }
|
||||||
|
|
||||||
:last-child { margin-bottom : 0; }
|
:last-child { margin-bottom : 0; }
|
||||||
}
|
}
|
||||||
@@ -458,7 +383,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
// *****************************
|
// *****************************
|
||||||
// * FOOTER
|
// * FOOTER
|
||||||
// *****************************/
|
// *****************************/
|
||||||
&:after {
|
&::after {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
bottom : 0px;
|
bottom : 0px;
|
||||||
left : 0px;
|
left : 0px;
|
||||||
@@ -503,18 +428,13 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
// ************************************/
|
// ************************************/
|
||||||
code {
|
code {
|
||||||
padding : 0px 4px;
|
padding : 0px 4px;
|
||||||
font-family : 'Courier New', "Courier", monospace;
|
font-size : 0.325cm;
|
||||||
font-size : 0.325;
|
|
||||||
color : #58180D;
|
color : #58180D;
|
||||||
overflow-wrap : break-word;
|
|
||||||
white-space : pre-wrap;
|
|
||||||
background-color : #FAF7EA;
|
background-color : #FAF7EA;
|
||||||
border-radius : 4px;
|
border-radius : 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
display : inline-block;
|
|
||||||
width : 100%;
|
|
||||||
padding : 0.15cm;
|
padding : 0.15cm;
|
||||||
margin-bottom : 2px;
|
margin-bottom : 2px;
|
||||||
border-style : solid;
|
border-style : solid;
|
||||||
@@ -535,19 +455,6 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
margin : 0px;
|
margin : 0px;
|
||||||
visibility : hidden;
|
visibility : hidden;
|
||||||
}
|
}
|
||||||
.columnSplit {
|
|
||||||
visibility : hidden;
|
|
||||||
-webkit-column-break-after : always;
|
|
||||||
break-after : always;
|
|
||||||
-moz-column-break-after : always;
|
|
||||||
}
|
|
||||||
//Avoid breaking up
|
|
||||||
blockquote,table {
|
|
||||||
z-index : 15;
|
|
||||||
-webkit-column-break-inside : avoid;
|
|
||||||
page-break-inside : avoid;
|
|
||||||
break-inside : avoid;
|
|
||||||
}
|
|
||||||
//Text indent right after table
|
//Text indent right after table
|
||||||
table + p { text-indent : 1em; }
|
table + p { text-indent : 1em; }
|
||||||
// Nested lists
|
// Nested lists
|
||||||
@@ -555,11 +462,6 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
margin-bottom : 0px;
|
margin-bottom : 0px;
|
||||||
margin-left : 1.5em;
|
margin-left : 1.5em;
|
||||||
}
|
}
|
||||||
li {
|
|
||||||
-webkit-column-break-inside : avoid;
|
|
||||||
page-break-inside : avoid;
|
|
||||||
break-inside : avoid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// *****************************
|
// *****************************
|
||||||
// * SPELL LIST
|
// * SPELL LIST
|
||||||
@@ -641,7 +543,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
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 : 0.85em;
|
||||||
@@ -654,7 +556,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
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';
|
||||||
font-size : 0.85cm;
|
font-size : 0.85cm;
|
||||||
font-weight : normal;
|
font-weight : normal;
|
||||||
color : white;
|
color : white;
|
||||||
@@ -687,7 +589,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
height : 1.7cm;
|
height : 1.7cm;
|
||||||
padding-top : 0.1cm;
|
padding-top : 0.1cm;
|
||||||
padding-left : 1cm;
|
padding-left : 1cm;
|
||||||
font-family : "NodestoCapsCondensed";
|
font-family : 'NodestoCapsCondensed';
|
||||||
font-size : 1cm;
|
font-size : 1cm;
|
||||||
font-weight : normal;
|
font-weight : normal;
|
||||||
color : white;
|
color : white;
|
||||||
@@ -704,7 +606,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
width : 70%;
|
width : 70%;
|
||||||
margin-right : auto;
|
margin-right : auto;
|
||||||
margin-left : auto;
|
margin-left : auto;
|
||||||
font-family : "Overpass";
|
font-family : 'Overpass';
|
||||||
font-size : 0.496cm;
|
font-size : 0.496cm;
|
||||||
color : white;
|
color : white;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
@@ -735,14 +637,14 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
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 : 0.85em;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-family : "NodestoCapsCondensed";
|
font-family : 'NodestoCapsCondensed';
|
||||||
font-size : 0.85cm;
|
font-size : 0.85cm;
|
||||||
font-weight : normal;
|
font-weight : normal;
|
||||||
letter-spacing : 0.5cm;
|
letter-spacing : 0.5cm;
|
||||||
@@ -791,7 +693,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
.blank { height : 1.4em; }
|
.blank { height : 1.4em; }
|
||||||
h1 {
|
h1 {
|
||||||
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 : 0.95em;
|
||||||
color : #ED1C24;
|
color : #ED1C24;
|
||||||
@@ -817,7 +719,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
border : none;
|
border : none;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
font-family : "Overpass";
|
font-family : 'Overpass';
|
||||||
font-size : 0.332cm;
|
font-size : 0.332cm;
|
||||||
line-height : 1.5em;
|
line-height : 1.5em;
|
||||||
}
|
}
|
||||||
@@ -841,7 +743,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
p {
|
p {
|
||||||
position : relative;
|
position : relative;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
font-family : "NodestoCapsWide";
|
font-family : 'NodestoCapsWide';
|
||||||
font-size : 0.4cm;
|
font-size : 0.4cm;
|
||||||
line-height : 1em;
|
line-height : 1em;
|
||||||
color : #FFFFFF;
|
color : #FFFFFF;
|
||||||
@@ -874,7 +776,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
h1 {
|
h1 {
|
||||||
position : relative;
|
position : relative;
|
||||||
margin-top : 0.4cm;
|
margin-top : 0.4cm;
|
||||||
font-family : "NodestoCapsCondensed";
|
font-family : 'NodestoCapsCondensed';
|
||||||
font-size : 2.3cm;
|
font-size : 2.3cm;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
@@ -885,7 +787,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
margin-top : -0.7em;
|
margin-top : -0.7em;
|
||||||
margin-right : auto;
|
margin-right : auto;
|
||||||
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 : 1.1em;
|
||||||
}
|
}
|
||||||
@@ -965,23 +867,14 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
// *****************************/
|
// *****************************/
|
||||||
.page {
|
.page {
|
||||||
dl {
|
dl {
|
||||||
padding-left : 1em;
|
|
||||||
line-height : 1.25em;
|
line-height : 1.25em;
|
||||||
white-space : pre-line;
|
& + * { margin-top : 0.17cm; }
|
||||||
& + * { margin-top : 0.28cm; }
|
|
||||||
}
|
}
|
||||||
dl + * { margin-top : 0.17cm; }
|
|
||||||
p + dl { margin-top : 0.17cm; }
|
p + dl { margin-top : 0.17cm; }
|
||||||
dt {
|
dt {
|
||||||
display : inline;
|
|
||||||
margin-right : 5px;
|
margin-right : 5px;
|
||||||
margin-left : -1em;
|
margin-left : -1em;
|
||||||
}
|
}
|
||||||
dd {
|
|
||||||
display : inline;
|
|
||||||
margin-left : 0px;
|
|
||||||
text-indent : 0px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// *****************************
|
// *****************************
|
||||||
@@ -1009,7 +902,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
outline : 1px solid #000000;
|
outline : 1px solid #000000;
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
font-family : "BookInsanityRemake";
|
font-family : 'BookInsanityRemake';
|
||||||
font-size : 0.45cm;
|
font-size : 0.45cm;
|
||||||
}
|
}
|
||||||
td { font-size : 0.7cm; }
|
td { font-size : 0.7cm; }
|
||||||
|
|||||||
@@ -111,6 +111,21 @@ module.exports = [
|
|||||||
icon : 'fas fa-code',
|
icon : 'fas fa-code',
|
||||||
gen : '<!-- This is a comment that will not be rendered into your brew. Hotkey (Ctrl/Cmd + /). -->'
|
gen : '<!-- This is a comment that will not be rendered into your brew. Hotkey (Ctrl/Cmd + /). -->'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name : 'Homebrewery Credit',
|
||||||
|
icon : 'fas fa-dice-d20',
|
||||||
|
gen : function(){
|
||||||
|
return dedent`
|
||||||
|
{{homebreweryCredits
|
||||||
|
Made With
|
||||||
|
|
||||||
|
{{homebreweryIcon}}
|
||||||
|
|
||||||
|
The Homebrewery
|
||||||
|
[Homebrewery.Naturalcrit.com](https://homebrewery.naturalcrit.com)
|
||||||
|
}}\n\n`;
|
||||||
|
},
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -289,6 +304,99 @@ module.exports = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
/**************** FONTS *************/
|
||||||
|
{
|
||||||
|
groupName : 'Fonts',
|
||||||
|
icon : 'fas fa-keyboard',
|
||||||
|
view : 'text',
|
||||||
|
snippets : [
|
||||||
|
{
|
||||||
|
name : 'Open Sans',
|
||||||
|
icon : 'font OpenSans',
|
||||||
|
gen : dedent`{{font-family:OpenSans Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Code Bold',
|
||||||
|
icon : 'font CodeBold',
|
||||||
|
gen : dedent`{{font-family:CodeBold Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Code Light',
|
||||||
|
icon : 'font CodeLight',
|
||||||
|
gen : dedent`{{font-family:CodeLight Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Scaly Sans Remake',
|
||||||
|
icon : 'font ScalySansRemake',
|
||||||
|
gen : dedent`{{font-family:ScalySansRemake Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Book Insanity Remake',
|
||||||
|
icon : 'font BookInsanityRemake',
|
||||||
|
gen : dedent`{{font-family:BookInsanityRemake Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'Mr Eaves Remake',
|
||||||
|
icon : 'font MrEavesRemake',
|
||||||
|
gen : dedent`{{font-family:MrEavesRemake Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Solbera Imitation Remake',
|
||||||
|
icon: 'font SolberaImitationRemake',
|
||||||
|
gen: dedent`{{font-family:SolberaImitationRemake Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Scaly Sans Small Caps Remake',
|
||||||
|
icon: 'font ScalySansSmallCapsRemake',
|
||||||
|
gen: dedent`{{font-family:ScalySansSmallCapsRemake Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Walter Turncoat',
|
||||||
|
icon: 'font WalterTurncoat',
|
||||||
|
gen: dedent`{{font-family:WalterTurncoat Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Lato',
|
||||||
|
icon: 'font Lato',
|
||||||
|
gen: dedent`{{font-family:Lato Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Courier',
|
||||||
|
icon: 'font Courier',
|
||||||
|
gen: dedent`{{font-family:Courier Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nodesto Caps Condensed',
|
||||||
|
icon: 'font NodestoCapsCondensed',
|
||||||
|
gen: dedent`{{font-family:NodestoCapsCondensed Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Overpass',
|
||||||
|
icon: 'font Overpass',
|
||||||
|
gen: dedent`{{font-family:Overpass Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Davek',
|
||||||
|
icon: 'font Davek',
|
||||||
|
gen: dedent`{{font-family:Davek Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Iokharic',
|
||||||
|
icon: 'font Iokharic',
|
||||||
|
gen: dedent`{{font-family:Iokharic Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rellanic',
|
||||||
|
icon: 'font Rellanic',
|
||||||
|
gen: dedent`{{font-family:Rellanic Dummy Text}}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Times New Roman',
|
||||||
|
icon: 'font TimesNewRoman',
|
||||||
|
gen: dedent`{{font-family:"Times New Roman" Dummy Text}}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
/**************** PAGE *************/
|
/**************** PAGE *************/
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@page { margin : 0; }
|
@page { margin : 0; }
|
||||||
body {
|
body { counter-reset : phb-page-numbers; }
|
||||||
counter-reset : phb-page-numbers;
|
* { -webkit-print-color-adjust : exact; }
|
||||||
}
|
|
||||||
*{
|
|
||||||
-webkit-print-color-adjust : exact;
|
|
||||||
}
|
|
||||||
|
|
||||||
//*****************************
|
//*****************************
|
||||||
// * MUSTACHE DIVS/SPANS
|
// * MUSTACHE DIVS/SPANS
|
||||||
@@ -23,9 +19,7 @@ body {
|
|||||||
break-inside : avoid;
|
break-inside : avoid;
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
img {
|
img { z-index : 0; }
|
||||||
z-index : 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.inline-block {
|
.inline-block {
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
@@ -38,26 +32,24 @@ body {
|
|||||||
column-count : 2;
|
column-count : 2;
|
||||||
}
|
}
|
||||||
.columnWrapper {
|
.columnWrapper {
|
||||||
|
column-gap : inherit;
|
||||||
max-height : 100%;
|
max-height : 100%;
|
||||||
column-span : all;
|
column-span : all;
|
||||||
columns : inherit;
|
columns : inherit;
|
||||||
column-gap : inherit;
|
|
||||||
column-fill : inherit;
|
column-fill : inherit;
|
||||||
}
|
}
|
||||||
.page {
|
.page {
|
||||||
.useColumns();
|
.useColumns();
|
||||||
height : 279.4mm;
|
|
||||||
width : 215.9mm;
|
|
||||||
padding : 1.4cm 1.9cm 1.7cm;
|
|
||||||
counter-increment : phb-page-numbers;
|
|
||||||
background-color : var(--HB_Color_Background);
|
|
||||||
position : relative;
|
position : relative;
|
||||||
z-index : 15;
|
z-index : 15;
|
||||||
box-sizing : border-box;
|
box-sizing : border-box;
|
||||||
|
width : 215.9mm;
|
||||||
|
height : 279.4mm;
|
||||||
|
padding : 1.4cm 1.9cm 1.7cm;
|
||||||
overflow : hidden;
|
overflow : hidden;
|
||||||
|
counter-increment : phb-page-numbers;
|
||||||
|
background-color : var(--HB_Color_Background);
|
||||||
text-rendering : optimizeLegibility;
|
text-rendering : optimizeLegibility;
|
||||||
page-break-before : always;
|
|
||||||
page-break-after : always;
|
|
||||||
contain : size;
|
contain : size;
|
||||||
}
|
}
|
||||||
//*****************************
|
//*****************************
|
||||||
@@ -65,38 +57,32 @@ body {
|
|||||||
// *****************************/
|
// *****************************/
|
||||||
.page {
|
.page {
|
||||||
p {
|
p {
|
||||||
overflow-wrap : break-word;
|
|
||||||
display : block;
|
display : block;
|
||||||
|
overflow-wrap : break-word;
|
||||||
}
|
}
|
||||||
strong{
|
strong { font-weight : bold; }
|
||||||
font-weight : bold;
|
em { font-style : italic; }
|
||||||
}
|
|
||||||
em{
|
|
||||||
font-style : italic;
|
|
||||||
}
|
|
||||||
sup {
|
sup {
|
||||||
vertical-align : super;
|
|
||||||
font-size : smaller;
|
font-size : smaller;
|
||||||
line-height : 0;
|
line-height : 0;
|
||||||
|
vertical-align : super;
|
||||||
}
|
}
|
||||||
sub {
|
sub {
|
||||||
vertical-align : sub;
|
|
||||||
font-size : smaller;
|
font-size : smaller;
|
||||||
line-height : 0;
|
line-height : 0;
|
||||||
|
vertical-align : sub;
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
|
padding-left : 1.4em;
|
||||||
list-style-position : outside; //Needed for multiline list items
|
list-style-position : outside; //Needed for multiline list items
|
||||||
list-style-type : disc;
|
list-style-type : disc;
|
||||||
padding-left : 1.4em;
|
|
||||||
}
|
}
|
||||||
ol {
|
ol {
|
||||||
|
padding-left : 1.4em;
|
||||||
list-style-position : outside;
|
list-style-position : outside;
|
||||||
list-style-type : decimal;
|
list-style-type : decimal;
|
||||||
padding-left : 1.4em;
|
|
||||||
}
|
|
||||||
img{
|
|
||||||
z-index : -1;
|
|
||||||
}
|
}
|
||||||
|
img { z-index : -1; }
|
||||||
|
|
||||||
//*****************************
|
//*****************************
|
||||||
// * HEADERS
|
// * HEADERS
|
||||||
@@ -105,21 +91,11 @@ body {
|
|||||||
font-weight : bold;
|
font-weight : bold;
|
||||||
line-height : 1.2em;
|
line-height : 1.2em;
|
||||||
}
|
}
|
||||||
h1{
|
h1 { font-size : 2em; }
|
||||||
font-size : 2em;
|
h2 { font-size : 1.5em; }
|
||||||
}
|
h3 { font-size : 1.17em; }
|
||||||
h2{
|
h4 { font-size : 1em; }
|
||||||
font-size : 1.5em;
|
h5 { font-size : 0.83em; }
|
||||||
}
|
|
||||||
h3{
|
|
||||||
font-size : 1.17em;
|
|
||||||
}
|
|
||||||
h4{
|
|
||||||
font-size : 1em;
|
|
||||||
}
|
|
||||||
h5{
|
|
||||||
font-size : 0.83em;
|
|
||||||
}
|
|
||||||
//*****************************
|
//*****************************
|
||||||
// * TABLE
|
// * TABLE
|
||||||
// *****************************/
|
// *****************************/
|
||||||
@@ -138,27 +114,25 @@ body {
|
|||||||
// * CODE BLOCKS
|
// * CODE BLOCKS
|
||||||
// ************************************/
|
// ************************************/
|
||||||
code {
|
code {
|
||||||
font-family : "Courier New", Courier, monospace;
|
font-family : 'Courier New', "Courier", monospace;
|
||||||
white-space : pre-wrap;
|
|
||||||
overflow-wrap : break-word;
|
overflow-wrap : break-word;
|
||||||
|
white-space : pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code {
|
||||||
width : 100%;
|
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
|
width : 100%;
|
||||||
}
|
}
|
||||||
//*****************************
|
//*****************************
|
||||||
// * EXTRAS
|
// * EXTRAS
|
||||||
// *****************************/
|
// *****************************/
|
||||||
.columnSplit {
|
.columnSplit {
|
||||||
|
margin-top : 0;
|
||||||
visibility : hidden;
|
visibility : hidden;
|
||||||
-webkit-column-break-after : always;
|
-webkit-column-break-after : always;
|
||||||
break-after : always;
|
break-after : always;
|
||||||
-moz-column-break-after : always;
|
-moz-column-break-after : always;
|
||||||
margin-top : 0;
|
& + * { margin-top : 0; }
|
||||||
& + * {
|
|
||||||
margin-top : 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//Avoid breaking up
|
//Avoid breaking up
|
||||||
blockquote,table {
|
blockquote,table {
|
||||||
@@ -180,41 +154,39 @@ body {
|
|||||||
|
|
||||||
/* Watermark */
|
/* Watermark */
|
||||||
.watermark {
|
.watermark {
|
||||||
|
position : absolute;
|
||||||
|
top : 0;
|
||||||
|
left : 0;
|
||||||
|
z-index : 500;
|
||||||
display : grid !important;
|
display : grid !important;
|
||||||
place-items : center;
|
place-items : center;
|
||||||
justify-content : center;
|
justify-content : center;
|
||||||
position : absolute;
|
|
||||||
margin : 0;
|
|
||||||
top : 0;
|
|
||||||
left : 0;
|
|
||||||
width : 100%;
|
width : 100%;
|
||||||
height : 100%;
|
height : 100%;
|
||||||
|
margin : 0;
|
||||||
font-size : 120px;
|
font-size : 120px;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
mix-blend-mode : overlay;
|
mix-blend-mode : overlay;
|
||||||
opacity : 30%;
|
opacity : 30%;
|
||||||
transform : rotate(-45deg);
|
transform : rotate(-45deg);
|
||||||
z-index : 500;
|
p { margin-bottom : none; }
|
||||||
p {
|
|
||||||
margin-bottom : none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Watercolor */
|
/* Watercolor */
|
||||||
[class*="watercolor"] {
|
[class*='watercolor'] {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
|
z-index : -2;
|
||||||
width : 2000px; /* dimensions need to be real big so the user can set */
|
width : 2000px; /* dimensions need to be real big so the user can set */
|
||||||
height : 2000px; /* height or width and the image will maintain aspect ratio */
|
height : 2000px; /* height or width and the image will maintain aspect ratio */
|
||||||
|
background-color : var(--HB_Color_WatercolorStain); /* default color */
|
||||||
|
background-size : cover;
|
||||||
-webkit-mask-image : var(--wc);
|
-webkit-mask-image : var(--wc);
|
||||||
-webkit-mask-size : contain;
|
-webkit-mask-size : contain;
|
||||||
-webkit-mask-repeat : no-repeat;
|
-webkit-mask-repeat : no-repeat;
|
||||||
mask-image : var(--wc);
|
mask-image : var(--wc);
|
||||||
mask-size : contain;
|
mask-size : contain;
|
||||||
mask-repeat : no-repeat;
|
mask-repeat : no-repeat;
|
||||||
background-size : cover;
|
|
||||||
background-color : var(--HB_Color_WatercolorStain); /*default color*/
|
|
||||||
--wc : @watercolor1; /* default image */
|
--wc : @watercolor1; /* default image */
|
||||||
z-index : -2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.watercolor1 { --wc : @watercolor1; }
|
.watercolor1 { --wc : @watercolor1; }
|
||||||
@@ -232,17 +204,16 @@ body {
|
|||||||
|
|
||||||
/* Image Masks */
|
/* Image Masks */
|
||||||
|
|
||||||
[class*="imageMask"] {
|
[class*='imageMask'] {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
height : 200%;
|
|
||||||
width : 200%;
|
|
||||||
left : 50%;
|
|
||||||
bottom : 50%;
|
bottom : 50%;
|
||||||
--rotation : 0;
|
left : 50%;
|
||||||
--revealer : none;
|
z-index : -1;
|
||||||
--checkerboard : none;
|
width : 200%;
|
||||||
--scaleX : 1;
|
height : 200%;
|
||||||
--scaleY : 1;
|
background-image : var(--checkerboard);
|
||||||
|
background-size : 20px;
|
||||||
|
transform : translateY(50%) translateX(-50%) rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||||
-webkit-mask-image : var(--wc), var(--revealer);
|
-webkit-mask-image : var(--wc), var(--revealer);
|
||||||
-webkit-mask-repeat : repeat-x;
|
-webkit-mask-repeat : repeat-x;
|
||||||
-webkit-mask-size : 50%; //Scale only X to fit page width, leave height at aspect ratio, designed to hang off the edge
|
-webkit-mask-size : 50%; //Scale only X to fit page width, leave height at aspect ratio, designed to hang off the edge
|
||||||
@@ -251,22 +222,23 @@ body {
|
|||||||
mask-repeat : repeat-x;
|
mask-repeat : repeat-x;
|
||||||
mask-size : 50%;
|
mask-size : 50%;
|
||||||
mask-position : 50% calc(50% - var(--offset));
|
mask-position : 50% calc(50% - var(--offset));
|
||||||
background-image : var(--checkerboard);
|
--rotation : 0;
|
||||||
background-size : 20px;
|
--revealer : none;
|
||||||
z-index : -1;
|
--checkerboard : none;
|
||||||
transform : translateY(50%) translateX(-50%) rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
--scaleX : 1;
|
||||||
|
--scaleY : 1;
|
||||||
& > p:has(img) {
|
& > p:has(img) {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
width : 50%;
|
|
||||||
height : 50%;
|
|
||||||
bottom : 50%;
|
bottom : 50%;
|
||||||
left : 50%;
|
left : 50%;
|
||||||
|
width : 50%;
|
||||||
|
height : 50%;
|
||||||
transform : translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
|
transform : translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
|
||||||
}
|
}
|
||||||
& img {
|
& img {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
display : block;
|
|
||||||
bottom : 0;
|
bottom : 0;
|
||||||
|
display : block;
|
||||||
}
|
}
|
||||||
&.bottom {
|
&.bottom {
|
||||||
--rotation : 0;
|
--rotation : 0;
|
||||||
@@ -285,27 +257,28 @@ body {
|
|||||||
& img {right : 0;}
|
& img {right : 0;}
|
||||||
}
|
}
|
||||||
&.revealImage {
|
&.revealImage {
|
||||||
--revealer : linear-gradient(0deg, rgba(0,0,0,.2) 0%, rgba(0,0,0,0.2));
|
--revealer : linear-gradient(0deg, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.2));
|
||||||
--checkerboard : url(/assets/waterColorMasks/missingImage.png); //shows any masked regions not filled by image
|
--checkerboard : url("/assets/waterColorMasks/missingImage.png"); //shows any masked regions not filled by image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.imageMaskEdge {
|
.imageMaskEdge {
|
||||||
&1 { --wc : url(/assets/waterColorMasks/edge/0001.webp); }
|
&1 { --wc : url("/assets/waterColorMasks/edge/0001.webp"); }
|
||||||
&2 { --wc : url(/assets/waterColorMasks/edge/0002.webp); }
|
&2 { --wc : url("/assets/waterColorMasks/edge/0002.webp"); }
|
||||||
&3 { --wc : url(/assets/waterColorMasks/edge/0003.webp); }
|
&3 { --wc : url("/assets/waterColorMasks/edge/0003.webp"); }
|
||||||
&4 { --wc : url(/assets/waterColorMasks/edge/0004.webp); }
|
&4 { --wc : url("/assets/waterColorMasks/edge/0004.webp"); }
|
||||||
&5 { --wc : url(/assets/waterColorMasks/edge/0005.webp); }
|
&5 { --wc : url("/assets/waterColorMasks/edge/0005.webp"); }
|
||||||
&6 { --wc : url(/assets/waterColorMasks/edge/0006.webp); }
|
&6 { --wc : url("/assets/waterColorMasks/edge/0006.webp"); }
|
||||||
&7 { --wc : url(/assets/waterColorMasks/edge/0007.webp); }
|
&7 { --wc : url("/assets/waterColorMasks/edge/0007.webp"); }
|
||||||
&8 { --wc : url(/assets/waterColorMasks/edge/0008.webp); }
|
&8 { --wc : url("/assets/waterColorMasks/edge/0008.webp"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*="imageMaskCenter"] {
|
[class*='imageMaskCenter'] {
|
||||||
|
bottom : calc(var(--offsetY));
|
||||||
|
left : calc(var(--offsetX));
|
||||||
width : 100%;
|
width : 100%;
|
||||||
height : 100%;
|
height : 100%;
|
||||||
left : calc(var(--offsetX));
|
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||||
bottom : calc(var(--offsetY));
|
|
||||||
-webkit-mask-image : var(--wc), var(--revealer);
|
-webkit-mask-image : var(--wc), var(--revealer);
|
||||||
-webkit-mask-repeat : no-repeat;
|
-webkit-mask-repeat : no-repeat;
|
||||||
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
@@ -314,14 +287,13 @@ body {
|
|||||||
mask-repeat : no-repeat;
|
mask-repeat : no-repeat;
|
||||||
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
mask-position : 50% 50%;
|
mask-position : 50% 50%;
|
||||||
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
|
||||||
|
|
||||||
& > p:has(img) {
|
& > p:has(img) {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
width : 100%;
|
|
||||||
height : 100%;
|
|
||||||
bottom : 0;
|
bottom : 0;
|
||||||
left : 0;
|
left : 0;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
transform : unset;
|
transform : unset;
|
||||||
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
||||||
rotate(calc(-1deg * var(--rotation)))
|
rotate(calc(-1deg * var(--rotation)))
|
||||||
@@ -331,31 +303,32 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.imageMaskCenter {
|
.imageMaskCenter {
|
||||||
&1 { --wc : url(/assets/waterColorMasks/center/0001.webp); }
|
&1 { --wc : url("/assets/waterColorMasks/center/0001.webp"); }
|
||||||
&2 { --wc : url(/assets/waterColorMasks/center/0002.webp); }
|
&2 { --wc : url("/assets/waterColorMasks/center/0002.webp"); }
|
||||||
&3 { --wc : url(/assets/waterColorMasks/center/0003.webp); }
|
&3 { --wc : url("/assets/waterColorMasks/center/0003.webp"); }
|
||||||
&4 { --wc : url(/assets/waterColorMasks/center/0004.webp); }
|
&4 { --wc : url("/assets/waterColorMasks/center/0004.webp"); }
|
||||||
&5 { --wc : url(/assets/waterColorMasks/center/0005.webp); }
|
&5 { --wc : url("/assets/waterColorMasks/center/0005.webp"); }
|
||||||
&6 { --wc : url(/assets/waterColorMasks/center/0006.webp); }
|
&6 { --wc : url("/assets/waterColorMasks/center/0006.webp"); }
|
||||||
&7 { --wc : url(/assets/waterColorMasks/center/0007.webp); }
|
&7 { --wc : url("/assets/waterColorMasks/center/0007.webp"); }
|
||||||
&8 { --wc : url(/assets/waterColorMasks/center/0008.webp); }
|
&8 { --wc : url("/assets/waterColorMasks/center/0008.webp"); }
|
||||||
&9 { --wc : url(/assets/waterColorMasks/center/0009.webp); }
|
&9 { --wc : url("/assets/waterColorMasks/center/0009.webp"); }
|
||||||
&10 { --wc : url(/assets/waterColorMasks/center/0010.webp); }
|
&10 { --wc : url("/assets/waterColorMasks/center/0010.webp"); }
|
||||||
&11 { --wc : url(/assets/waterColorMasks/center/0011.webp); }
|
&11 { --wc : url("/assets/waterColorMasks/center/0011.webp"); }
|
||||||
&12 { --wc : url(/assets/waterColorMasks/center/0012.webp); }
|
&12 { --wc : url("/assets/waterColorMasks/center/0012.webp"); }
|
||||||
&13 { --wc : url(/assets/waterColorMasks/center/0013.webp); }
|
&13 { --wc : url("/assets/waterColorMasks/center/0013.webp"); }
|
||||||
&14 { --wc : url(/assets/waterColorMasks/center/0014.webp); }
|
&14 { --wc : url("/assets/waterColorMasks/center/0014.webp"); }
|
||||||
&15 { --wc : url(/assets/waterColorMasks/center/0015.webp); }
|
&15 { --wc : url("/assets/waterColorMasks/center/0015.webp"); }
|
||||||
&16 { --wc : url(/assets/waterColorMasks/center/0016.webp); }
|
&16 { --wc : url("/assets/waterColorMasks/center/0016.webp"); }
|
||||||
&special { --wc : url(/assets/waterColorMasks/center/special.webp); }
|
&special { --wc : url("/assets/waterColorMasks/center/special.webp"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[class*="imageMaskCorner"] {
|
[class*='imageMaskCorner'] {
|
||||||
height : 200%;
|
|
||||||
width : 200%;
|
|
||||||
left : calc(-50% + var(--offsetX));
|
|
||||||
bottom : calc(-50% + var(--offsetY));
|
bottom : calc(-50% + var(--offsetY));
|
||||||
|
left : calc(-50% + var(--offsetX));
|
||||||
|
width : 200%;
|
||||||
|
height : 200%;
|
||||||
|
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||||
-webkit-mask-image : var(--wc), var(--revealer);
|
-webkit-mask-image : var(--wc), var(--revealer);
|
||||||
-webkit-mask-repeat : no-repeat;
|
-webkit-mask-repeat : no-repeat;
|
||||||
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
@@ -364,12 +337,11 @@ body {
|
|||||||
mask-repeat : no-repeat;
|
mask-repeat : no-repeat;
|
||||||
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||||
mask-position : 50% 50%;
|
mask-position : 50% 50%;
|
||||||
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
|
||||||
& > p:has(img) {
|
& > p:has(img) {
|
||||||
|
bottom : 25%;
|
||||||
|
left : 25%;
|
||||||
width : 50%;
|
width : 50%;
|
||||||
height : 50%; //Complex transform below to handle mix of % and cm offsets
|
height : 50%; //Complex transform below to handle mix of % and cm offsets
|
||||||
left : 25%;
|
|
||||||
bottom : 25%;
|
|
||||||
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
||||||
rotate(calc(-1deg * var(--rotation)))
|
rotate(calc(-1deg * var(--rotation)))
|
||||||
translateX(calc(-1 * var(--offsetX)))
|
translateX(calc(-1 * var(--offsetX)))
|
||||||
@@ -377,43 +349,43 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.imageMaskCorner {
|
.imageMaskCorner {
|
||||||
&1 { --wc : url(/assets/waterColorMasks/corner/0001.webp); }
|
&1 { --wc : url("/assets/waterColorMasks/corner/0001.webp"); }
|
||||||
&2 { --wc : url(/assets/waterColorMasks/corner/0002.webp); }
|
&2 { --wc : url("/assets/waterColorMasks/corner/0002.webp"); }
|
||||||
&3 { --wc : url(/assets/waterColorMasks/corner/0003.webp); }
|
&3 { --wc : url("/assets/waterColorMasks/corner/0003.webp"); }
|
||||||
&4 { --wc : url(/assets/waterColorMasks/corner/0004.webp); }
|
&4 { --wc : url("/assets/waterColorMasks/corner/0004.webp"); }
|
||||||
&5 { --wc : url(/assets/waterColorMasks/corner/0005.webp); }
|
&5 { --wc : url("/assets/waterColorMasks/corner/0005.webp"); }
|
||||||
&6 { --wc : url(/assets/waterColorMasks/corner/0006.webp); }
|
&6 { --wc : url("/assets/waterColorMasks/corner/0006.webp"); }
|
||||||
&7 { --wc : url(/assets/waterColorMasks/corner/0007.webp); }
|
&7 { --wc : url("/assets/waterColorMasks/corner/0007.webp"); }
|
||||||
&8 { --wc : url(/assets/waterColorMasks/corner/0008.webp); }
|
&8 { --wc : url("/assets/waterColorMasks/corner/0008.webp"); }
|
||||||
&9 { --wc : url(/assets/waterColorMasks/corner/0009.webp); }
|
&9 { --wc : url("/assets/waterColorMasks/corner/0009.webp"); }
|
||||||
&10 { --wc : url(/assets/waterColorMasks/corner/0010.webp); }
|
&10 { --wc : url("/assets/waterColorMasks/corner/0010.webp"); }
|
||||||
&11 { --wc : url(/assets/waterColorMasks/corner/0011.webp); }
|
&11 { --wc : url("/assets/waterColorMasks/corner/0011.webp"); }
|
||||||
&12 { --wc : url(/assets/waterColorMasks/corner/0012.webp); }
|
&12 { --wc : url("/assets/waterColorMasks/corner/0012.webp"); }
|
||||||
&13 { --wc : url(/assets/waterColorMasks/corner/0013.webp); }
|
&13 { --wc : url("/assets/waterColorMasks/corner/0013.webp"); }
|
||||||
&14 { --wc : url(/assets/waterColorMasks/corner/0014.webp); }
|
&14 { --wc : url("/assets/waterColorMasks/corner/0014.webp"); }
|
||||||
&15 { --wc : url(/assets/waterColorMasks/corner/0015.webp); }
|
&15 { --wc : url("/assets/waterColorMasks/corner/0015.webp"); }
|
||||||
&16 { --wc : url(/assets/waterColorMasks/corner/0016.webp); }
|
&16 { --wc : url("/assets/waterColorMasks/corner/0016.webp"); }
|
||||||
&17 { --wc : url(/assets/waterColorMasks/corner/0017.webp); }
|
&17 { --wc : url("/assets/waterColorMasks/corner/0017.webp"); }
|
||||||
&18 { --wc : url(/assets/waterColorMasks/corner/0018.webp); }
|
&18 { --wc : url("/assets/waterColorMasks/corner/0018.webp"); }
|
||||||
&19 { --wc : url(/assets/waterColorMasks/corner/0019.webp); }
|
&19 { --wc : url("/assets/waterColorMasks/corner/0019.webp"); }
|
||||||
&20 { --wc : url(/assets/waterColorMasks/corner/0020.webp); }
|
&20 { --wc : url("/assets/waterColorMasks/corner/0020.webp"); }
|
||||||
&21 { --wc : url(/assets/waterColorMasks/corner/0021.webp); }
|
&21 { --wc : url("/assets/waterColorMasks/corner/0021.webp"); }
|
||||||
&22 { --wc : url(/assets/waterColorMasks/corner/0022.webp); }
|
&22 { --wc : url("/assets/waterColorMasks/corner/0022.webp"); }
|
||||||
&23 { --wc : url(/assets/waterColorMasks/corner/0023.webp); }
|
&23 { --wc : url("/assets/waterColorMasks/corner/0023.webp"); }
|
||||||
&24 { --wc : url(/assets/waterColorMasks/corner/0024.webp); }
|
&24 { --wc : url("/assets/waterColorMasks/corner/0024.webp"); }
|
||||||
&25 { --wc : url(/assets/waterColorMasks/corner/0025.webp); }
|
&25 { --wc : url("/assets/waterColorMasks/corner/0025.webp"); }
|
||||||
&26 { --wc : url(/assets/waterColorMasks/corner/0026.webp); }
|
&26 { --wc : url("/assets/waterColorMasks/corner/0026.webp"); }
|
||||||
&27 { --wc : url(/assets/waterColorMasks/corner/0027.webp); }
|
&27 { --wc : url("/assets/waterColorMasks/corner/0027.webp"); }
|
||||||
&28 { --wc : url(/assets/waterColorMasks/corner/0028.webp); }
|
&28 { --wc : url("/assets/waterColorMasks/corner/0028.webp"); }
|
||||||
&29 { --wc : url(/assets/waterColorMasks/corner/0029.webp); }
|
&29 { --wc : url("/assets/waterColorMasks/corner/0029.webp"); }
|
||||||
&30 { --wc : url(/assets/waterColorMasks/corner/0030.webp); }
|
&30 { --wc : url("/assets/waterColorMasks/corner/0030.webp"); }
|
||||||
&31 { --wc : url(/assets/waterColorMasks/corner/0031.webp); }
|
&31 { --wc : url("/assets/waterColorMasks/corner/0031.webp"); }
|
||||||
&32 { --wc : url(/assets/waterColorMasks/corner/0032.webp); }
|
&32 { --wc : url("/assets/waterColorMasks/corner/0032.webp"); }
|
||||||
&33 { --wc : url(/assets/waterColorMasks/corner/0033.webp); }
|
&33 { --wc : url("/assets/waterColorMasks/corner/0033.webp"); }
|
||||||
&34 { --wc : url(/assets/waterColorMasks/corner/0034.webp); }
|
&34 { --wc : url("/assets/waterColorMasks/corner/0034.webp"); }
|
||||||
&35 { --wc : url(/assets/waterColorMasks/corner/0035.webp); }
|
&35 { --wc : url("/assets/waterColorMasks/corner/0035.webp"); }
|
||||||
&36 { --wc : url(/assets/waterColorMasks/corner/0036.webp); }
|
&36 { --wc : url("/assets/waterColorMasks/corner/0036.webp"); }
|
||||||
&37 { --wc : url(/assets/waterColorMasks/corner/0037.webp); }
|
&37 { --wc : url("/assets/waterColorMasks/corner/0037.webp"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,9 +416,7 @@ body {
|
|||||||
.blank {
|
.blank {
|
||||||
height : 1em;
|
height : 1em;
|
||||||
margin-top : 0;
|
margin-top : 0;
|
||||||
& + * {
|
& + * { margin-top : 0; }
|
||||||
margin-top : 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,8 +428,35 @@ body {
|
|||||||
column-span : all;
|
column-span : all;
|
||||||
display : block;
|
display : block;
|
||||||
margin-bottom : 1em;
|
margin-bottom : 1em;
|
||||||
&+* {
|
& + * { margin-top : 0; }
|
||||||
margin-top : 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//*****************************
|
||||||
|
//* CREDITS
|
||||||
|
//*****************************/
|
||||||
|
.page .homebreweryCredits {
|
||||||
|
p {
|
||||||
|
font-family : 'NodestoCapsWide';
|
||||||
|
font-size : 0.4cm;
|
||||||
|
line-height : 1em;
|
||||||
|
text-align : center;
|
||||||
|
text-indent : 0;
|
||||||
|
letter-spacing : 0.08em;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color : inherit;
|
||||||
|
text-decoration : none;
|
||||||
|
&:hover { text-decoration : underline; }
|
||||||
|
}
|
||||||
|
.homebreweryIcon {
|
||||||
|
display : block;
|
||||||
|
height : 1.5cm;
|
||||||
|
margin : 0 auto;
|
||||||
|
background-color : black;
|
||||||
|
-webkit-mask : url("/assets/naturalCritLogoWhite.svg") center / contain no-repeat;
|
||||||
|
mask : url("/assets/naturalCritLogoWhite.svg") center / contain no-repeat;
|
||||||
|
}
|
||||||
|
.homebreweryIcon.red { background-color : red; }
|
||||||
|
.homebreweryIcon.gold { background-image : linear-gradient(to top left, brown 22.5%, gold 40%, white 60%, gold 67.5%, brown 82.5%); }
|
||||||
}
|
}
|
||||||
|
|||||||
129
themes/codeMirror/customThemes/darkbrewery-v301.css
Normal file
129
themes/codeMirror/customThemes/darkbrewery-v301.css
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/* Main BG color and normal text color */
|
||||||
|
.CodeMirror {
|
||||||
|
background: #293134;
|
||||||
|
color: #91A6AA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brew BG */
|
||||||
|
.brewRenderer {
|
||||||
|
background-color: #293134;
|
||||||
|
}
|
||||||
|
/* Blinking cursor */
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid #e0e2e4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HB DARK NAV START*/
|
||||||
|
|
||||||
|
/* Bars at the top */
|
||||||
|
.snippetBar {
|
||||||
|
background-color: #2F393C;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
nav {
|
||||||
|
background-color: #293134;
|
||||||
|
}
|
||||||
|
nav .navItem {
|
||||||
|
background-color: #293134;
|
||||||
|
}
|
||||||
|
/* Fix for Homebrewery custom Snippet icons */
|
||||||
|
.snippetBar .fac {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
.snippetBar .snippetGroup .dropdown {
|
||||||
|
background-color: #2F393C;
|
||||||
|
}
|
||||||
|
/* HB DARK NAV END */
|
||||||
|
|
||||||
|
/* Line number stuff */
|
||||||
|
.CodeMirror-gutter-elt {
|
||||||
|
color: #81969A;
|
||||||
|
}
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
background-color: #293134;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter {
|
||||||
|
background-color: #293134;
|
||||||
|
}
|
||||||
|
/* column splits */
|
||||||
|
.editor .codeEditor .columnSplit {
|
||||||
|
font-style: italic;
|
||||||
|
color: inherit;
|
||||||
|
background-color:#1f5763;
|
||||||
|
border-bottom: #299 solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colors for headings and such */
|
||||||
|
/* ###Headings */
|
||||||
|
.cm-s-default .cm-header {
|
||||||
|
color: #c51b1b;
|
||||||
|
-webkit-text-stroke-width: 0.1px;
|
||||||
|
-webkit-text-stroke-color: #000;
|
||||||
|
}
|
||||||
|
/* bold points */
|
||||||
|
.cm-header, .cm-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #309dd2;
|
||||||
|
}
|
||||||
|
/* Link headings */
|
||||||
|
.cm-s-default .cm-link {
|
||||||
|
color: #dd6300;
|
||||||
|
}
|
||||||
|
/* links */
|
||||||
|
.cm-s-default .cm-string {
|
||||||
|
color: #aa8261;
|
||||||
|
}
|
||||||
|
/*@import*/
|
||||||
|
.cm-s-default .cm-def {
|
||||||
|
color:#2986cc;
|
||||||
|
}
|
||||||
|
/* Bullets and such */
|
||||||
|
.cm-s-default .cm-variable-2 {
|
||||||
|
color: #3cbf30;
|
||||||
|
}
|
||||||
|
/* blocks */
|
||||||
|
.editor .codeEditor .block:not(.cm-comment) {
|
||||||
|
color: #e3e3e3;
|
||||||
|
}
|
||||||
|
/* inline blocks */
|
||||||
|
.editor .codeEditor .inline-block {
|
||||||
|
color: #e3e3e3;
|
||||||
|
}
|
||||||
|
/* Tags (divs) */
|
||||||
|
.cm-s-default .cm-tag {
|
||||||
|
color: #e3ff00;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-attribute {
|
||||||
|
color: #e3ff00;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-atom {
|
||||||
|
color:#000;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-qualifier{
|
||||||
|
color:#ee1919;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-comment{
|
||||||
|
color:#bbc700;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-keyword {
|
||||||
|
color:#c302df;
|
||||||
|
background-color:#b1b1b1;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-property.cm-error {
|
||||||
|
color:#c50202;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-foldmarker {
|
||||||
|
color:#f0ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* New page */
|
||||||
|
.editor .codeEditor .pageLine {
|
||||||
|
background: #000;
|
||||||
|
color:#000;
|
||||||
|
border-bottom: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-default .cm-builtin {
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"cobalt",
|
"cobalt",
|
||||||
"colorforth",
|
"colorforth",
|
||||||
"darcula",
|
"darcula",
|
||||||
|
"darkbrewery-v301",
|
||||||
"dracula",
|
"dracula",
|
||||||
"duotone-dark",
|
"duotone-dark",
|
||||||
"duotone-light",
|
"duotone-light",
|
||||||
|
|||||||
BIN
themes/fonts/icon fonts/Elderberry-Inn-Icons.woff2
Normal file
BIN
themes/fonts/icon fonts/Elderberry-Inn-Icons.woff2
Normal file
Binary file not shown.
224
themes/fonts/icon fonts/font-icons.less
Normal file
224
themes/fonts/icon fonts/font-icons.less
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/* 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'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user