0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 20:42:43 +00:00

Merge branch 'master' into RPG-Awesome-Redux

This commit is contained in:
Gazook89
2024-05-15 20:19:00 -05:00
30 changed files with 18023 additions and 15671 deletions

View File

@@ -14,6 +14,9 @@ const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
const Frame = require('react-frame-component').default;
const dedent = require('dedent-tabs').default;
const DOMPurify = require('dompurify');
const purifyConfig = { FORCE_BODY: true, SANITIZE_DOM: false };
const Themes = require('themes/themes.json');
const PAGE_HEIGHT = 1056;
@@ -33,8 +36,9 @@ const BrewPage = (props)=>{
index : 0,
...props
};
const cleanText = DOMPurify.sanitize(props.contents, purifyConfig);
return <div className={props.className} id={`p${props.index + 1}`} >
<div className='columnWrapper' dangerouslySetInnerHTML={{ __html: props.contents }} />
<div className='columnWrapper' dangerouslySetInnerHTML={{ __html: cleanText }} />
</div>;
};
@@ -102,13 +106,6 @@ const BrewRenderer = (props)=>{
return false;
};
const sanitizeScriptTags = (content)=>{
return content
?.replace(/<script/ig, '&lt;script')
.replace(/<\/script>/ig, '&lt;/script&gt;')
|| '';
};
const renderPageInfo = ()=>{
return <div className='pageInfo' ref={mainRef}>
<div>
@@ -128,19 +125,18 @@ const BrewRenderer = (props)=>{
const renderStyle = ()=>{
if(!props.style) return;
const cleanStyle = sanitizeScriptTags(props.style);
const cleanStyle = DOMPurify.sanitize(props.style, purifyConfig);
//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>` }} />;
};
const renderPage = (pageText, index)=>{
let cleanPageText = sanitizeScriptTags(pageText);
if(props.renderer == 'legacy') {
const html = MarkdownLegacy.render(cleanPageText);
const html = MarkdownLegacy.render(pageText);
return <BrewPage className='page phb' index={index} key={index} contents={html} />;
} else {
cleanPageText += `\n\n&nbsp;\n\\column\n&nbsp;`; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
const html = Markdown.render(cleanPageText, index);
pageText += `\n\n&nbsp;\n\\column\n&nbsp;`; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
const html = Markdown.render(pageText, index);
return <BrewPage className='page' index={index} key={index} contents={html} />;
}
};
@@ -211,11 +207,11 @@ const BrewRenderer = (props)=>{
<RenderWarnings />
<NotificationPopup />
</div>
<link href={`/themes/${rendererPath}/Blank/style.css`} type="text/css" rel='stylesheet'/>
<link href={`/themes/${rendererPath}/Blank/style.css`} type='text/css' rel='stylesheet'/>
{baseThemePath &&
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} type="text/css" rel='stylesheet'/>
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} type='text/css' rel='stylesheet'/>
}
<link href={`/themes/${rendererPath}/${themePath}/style.css`} type="text/css" rel='stylesheet'/>
<link href={`/themes/${rendererPath}/${themePath}/style.css`} type='text/css' rel='stylesheet'/>
{/* Apply CSS from Style tab and render pages from Markdown tab */}
{state.isMounted

View File

@@ -5,6 +5,7 @@ const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const dedent = require('dedent-tabs').default;
const Markdown = require('../../../shared/naturalcrit/markdown.js');
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
@@ -219,6 +220,34 @@ const Editor = createClass({
endCh = match.index+match[0].length;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
}
// Emojis
if(line.match(/:[^\s:]+:/g)) {
let startIndex = line.indexOf(':');
const emojiRegex = /:[^\s:]+:/gy;
while (startIndex >= 0) {
emojiRegex.lastIndex = startIndex;
let match = emojiRegex.exec(line);
if (match) {
let tokens = Markdown.marked.lexer(match[0]);
tokens = tokens[0].tokens.filter(t => t.type == 'emoji')
if (!tokens.length)
return;
let startPos = { line: lineNumber, ch: match.index };
let endPos = { line: lineNumber, ch: match.index + match[0].length };
// Iterate over conflicting marks and clear them
var marks = codeMirror.findMarks(startPos, endPos);
marks.forEach(function(marker) {
marker.clear();
});
codeMirror.markText(startPos, endPos, { className: 'emoji' });
}
startIndex = line.indexOf(':', Math.max(startIndex + 1, emojiRegex.lastIndex));
}
}
}
});
});

View File

@@ -43,6 +43,16 @@
font-weight : bold;
color : green;
}
.emoji:not(.cm-comment) {
margin-left : 2px;
color : #360034;
background : #ffc8ff;
border-radius : 6px;
font-weight : bold;
padding-bottom : 1px;
outline-offset : -2px;
outline : solid 2px #ff96fc;
}
.superscript:not(.cm-comment) {
font-weight : bold;
color : goldenrod;

View File

@@ -1,41 +1,25 @@
require('./errorPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const UIPage = require('../basePages/uiPage/uiPage.jsx');
const Markdown = require('../../../../shared/naturalcrit/markdown.js');
const React = require('react');
const UIPage = require('../basePages/uiPage/uiPage.jsx');
const Markdown = require('../../../../shared/naturalcrit/markdown.js');
const ErrorIndex = require('./errors/errorIndex.js');
const ErrorPage = createClass({
displayName : 'ErrorPage',
const ErrorPage = ({ brew })=>{
// Retrieving the error text based on the brew's error code from ErrorIndex
const errorText = ErrorIndex({ brew })[brew.HBErrorCode.toString()] || '';
getDefaultProps : function() {
return {
ver : '0.0.0',
errorId : '',
text : '# Oops \n We could not find a brew with that id. **Sorry!**',
error : {}
};
},
render : function(){
const errorText = ErrorIndex(this.props)[this.props.brew.HBErrorCode.toString()] || '';
return <UIPage brew={{ title: 'Crit Fail!' }}>
return (
<UIPage brew={{ title: 'Crit Fail!' }}>
<div className='dataGroup'>
<div className='errorTitle'>
<h1>{`Error ${this.props.brew.status || '000'}`}</h1>
<h4>{this.props.brew.text || 'No error text'}</h4>
<h1>{`Error ${brew?.status || '000'}`}</h1>
<h4>{brew?.text || 'No error text'}</h4>
</div>
<hr />
<div dangerouslySetInnerHTML={{ __html: Markdown.render(errorText) }} />
</div>
</UIPage>;
}
});
</UIPage>
);
};
module.exports = ErrorPage;

View File

@@ -47,6 +47,19 @@ const SharePage = createClass({
this.props.brew.shareId;
},
renderEditLink : function(){
if(!this.props.brew.editId) return;
let editLink = this.props.brew.editId;
if(this.props.brew.googleId && !this.props.brew.stubbed) {
editLink = this.props.brew.googleId + editLink;
}
return <Nav.item color='orange' icon='fas fa-pencil-alt' href={`/edit/${editLink}`}>
edit
</Nav.item>;
},
render : function(){
return <div className='sharePage sitePage'>
<Meta name='robots' content='noindex, nofollow' />
@@ -64,13 +77,14 @@ const SharePage = createClass({
<Nav.item color='red' icon='fas fa-code'>
source
</Nav.item>
<Nav.item color='blue' href={`/source/${this.processShareId()}`}>
<Nav.item color='blue' icon='fas fa-eye' href={`/source/${this.processShareId()}`}>
view
</Nav.item>
<Nav.item color='blue' href={`/download/${this.processShareId()}`}>
{this.renderEditLink()}
<Nav.item color='blue' icon='fas fa-download' href={`/download/${this.processShareId()}`}>
download
</Nav.item>
<Nav.item color='blue' href={`/new/${this.processShareId()}`}>
<Nav.item color='blue' icon='fas fa-clone' href={`/new/${this.processShareId()}`}>
clone to new
</Nav.item>
</Nav.dropdown>

30314
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,11 +27,12 @@
"test:dev": "jest --verbose --watch",
"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:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
"test:mustache-syntax:injection": "jest '.*(mustache-syntax).*' -t '^Injection:.*' --verbose --noStackTrace",
"test:mustache-syntax": "jest \".*(mustache-syntax).*\" --verbose --noStackTrace",
"test:mustache-syntax:inline": "jest \".*(mustache-syntax).*\" -t '^Inline:.*' --verbose --noStackTrace",
"test:mustache-syntax:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace",
"test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace",
"test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
"test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace",
"test:route": "jest tests/routes/static-pages.test.js --verbose",
"phb": "node scripts/phb.js",
"prod": "set NODE_ENV=production && npm run build",
@@ -81,17 +82,18 @@
]
},
"dependencies": {
"@babel/core": "^7.24.4",
"@babel/core": "^7.24.5",
"@babel/plugin-transform-runtime": "^7.24.3",
"@babel/preset-env": "^7.24.4",
"@babel/preset-env": "^7.24.5",
"@babel/preset-react": "^7.24.1",
"@googleapis/drive": "^8.7.0",
"@googleapis/drive": "^8.8.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
"cookie-parser": "^1.4.6",
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.3",
"dompurify": "^3.1.1",
"expr-eval": "^2.0.2",
"express": "^4.19.2",
"express-async-handler": "^1.2.0",
@@ -108,20 +110,20 @@
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
"mongoose": "^8.3.1",
"mongoose": "^8.3.3",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
"react-router-dom": "6.22.3",
"react-router-dom": "6.23.0",
"sanitize-filename": "1.6.3",
"superagent": "^8.1.2",
"superagent": "^9.0.2",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.2.0",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-react": "^7.34.1",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
@@ -130,6 +132,6 @@
"stylelint-config-recess-order": "^4.6.0",
"stylelint-config-recommended": "^13.0.0",
"stylelint-stylistic": "^0.4.3",
"supertest": "^6.3.4"
"supertest": "^7.0.0"
}
}

View File

@@ -23,9 +23,9 @@ const { splitTextStyleAndMetadata } = require('../shared/helpers.js');
const sanitizeBrew = (brew, accessType)=>{
brew._id = undefined;
brew.__v = undefined;
if(accessType !== 'edit'){
if(accessType !== 'edit' && accessType !== 'shareAuthor') {
brew.editId = undefined;
}
}
return brew;
};
@@ -307,7 +307,6 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
//Share Page
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
const { brew } = req;
req.ogMeta = { ...defaultMetaTags,
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
@@ -326,7 +325,8 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
await HomebrewModel.increaseView({ shareId: brew.shareId });
}
};
sanitizeBrew(req.brew, 'share');
brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew);
return next();
}));

View File

@@ -0,0 +1,82 @@
const diceFont = require('../../../themes/fonts/iconFonts/diceFont.js');
const elderberryInn = require('../../../themes/fonts/iconFonts/elderberryInn.js');
const fontAwesome = require('../../../themes/fonts/iconFonts/fontAwesome.js');
const emojis = {
...diceFont,
...elderberryInn,
...fontAwesome
};
const showAutocompleteEmoji = function(CodeMirror, editor) {
CodeMirror.commands.autocomplete = function(editor) {
editor.showHint({
completeSingle : false,
hint : function(editor) {
const cursor = editor.getCursor();
const line = cursor.line;
const lineContent = editor.getLine(line);
const start = lineContent.lastIndexOf(':', cursor.ch - 1) + 1;
const end = cursor.ch;
const currentWord = lineContent.slice(start, end);
const list = Object.keys(emojis).filter(function(emoji) {
return emoji.toLowerCase().indexOf(currentWord.toLowerCase()) >= 0;
}).sort((a, b)=>{
const lowerA = a.replace(/\d+/g, function(match) { // Temporarily convert any numbers in emoji string
return match.padStart(4, '0'); // to 4-digits, left-padded with 0's, to aid in
}).toLowerCase(); // sorting numbers, i.e., "d6, d10, d20", not "d10, d20, d6"
const lowerB = b.replace(/\d+/g, function(match) { // Also make lowercase for case-insensitive alpha sorting
return match.padStart(4, '0');
}).toLowerCase();
if(lowerA < lowerB)
return -1;
return 1;
}).map(function(emoji) {
return {
text : `${emoji}:`, // Text to output to editor when option is selected
render : function(element, self, data) { // How to display the option in the dropdown
const div = document.createElement('div');
div.innerHTML = `<i class="emojiPreview ${emojis[emoji]}"></i> ${emoji}`;
element.appendChild(div);
}
};
});
return {
list : list.length ? list : [],
from : CodeMirror.Pos(line, start),
to : CodeMirror.Pos(line, end)
};
}
});
};
editor.on('inputRead', function(instance, change) {
const cursor = editor.getCursor();
const line = editor.getLine(cursor.line);
// Get the text from the start of the line to the cursor
const textToCursor = line.slice(0, cursor.ch);
// Do not autosuggest emojis in curly span/div/injector properties
if(line.includes('{')) {
const curlyToCursor = textToCursor.slice(textToCursor.indexOf(`{`));
const curlySpanRegex = /{(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\1$/g;
if(curlySpanRegex.test(curlyToCursor))
return;
}
// Check if the text ends with ':xyz'
if(/:[^\s:]+$/.test(textToCursor)) {
CodeMirror.commands.autocomplete(editor);
}
});
};
module.exports = {
showAutocompleteEmoji
};

View File

@@ -5,7 +5,7 @@ const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const closeTag = require('./close-tag');
const autoCompleteEmojis = require('./autocomplete-emoji');
const autoCompleteEmoji = require('./autocompleteEmoji');
let CodeMirror;
if(typeof window !== 'undefined'){
@@ -180,10 +180,10 @@ const CodeEditor = createClass({
// return el;
// }
});
// Add custom behaviors (auto-close curlies and auto-complete emojis)
closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror);
autoCompleteEmojis.showEmojiAutocomplete(CodeMirror, this.codeMirror);
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
@@ -442,7 +442,7 @@ const CodeEditor = createClass({
render : function(){
return <>
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} type="text/css" rel='stylesheet' />
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} type='text/css' rel='stylesheet' />
<div className='codeEditor' ref='editor' style={this.props.style}/>
</>;
}

View File

@@ -5,9 +5,9 @@
@import (less) 'codemirror/addon/hint/show-hint.css';
//Icon fonts included so they can appear in emoji autosuggest dropdown
@import (less) './themes/fonts/icon fonts/diceFont.less';
@import (less) './themes/fonts/icon fonts/elderberryInn.less';
@import (less) './themes/fonts/icon fonts/raRedux.less';
@import (less) './themes/fonts/iconFonts/diceFont.less';
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
@import (less) './themes/fonts/iconFonts/raRedux.less';
@keyframes sourceMoveAnimation {
50% {background-color: red; color: white;}

View File

@@ -4,12 +4,13 @@ const Marked = require('marked');
const MarkedExtendedTables = require('marked-extended-tables');
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
const { markedEmoji: MarkedEmojis} = require('marked-emoji');
const { markedEmoji: MarkedEmojis } = require('marked-emoji');
//Icon fonts included so they can appear in emoji autosuggest dropdown
const diceFont = require('../../themes/fonts/icon fonts/diceFont.js');
const elderberryInn = require('../../themes/fonts/icon fonts/elderberryInn.js');
const raRedux = require('../../themes/fonts/icon fonts/raRedux.js');
const diceFont = require('../../themes/fonts/iconFonts/diceFont.js');
const elderberryInn = require('../../themes/fonts/iconFonts/elderberryInn.js');
const fontAwesome = require('../../themes/fonts/iconFonts/fontAwesome.js');
const raRedux = require('../../themes/fonts/iconFonts/raRedux.js');
const MathParser = require('expr-eval').Parser;
const renderer = new Marked.Renderer();
@@ -57,7 +58,7 @@ renderer.html = function (html) {
return html;
};
// Don't wrap {{ Divs or {{ empty Spans in <p> tags
// Don't wrap {{ Spans alone on a line, or {{ Divs in <p> tags
renderer.paragraph = function(text){
let match;
if(text.startsWith('<div') || text.startsWith('</div'))
@@ -106,13 +107,13 @@ const mustacheSpans = {
if(match) {
//Find closing delimiter
let blockCount = 0;
let tags = '';
let tags = {};
let endTags = 0;
let endToken = 0;
let delim;
while (delim = inlineRegex.exec(match[0])) {
if(!tags) {
tags = `${processStyleTags(delim[0].substring(2))}`;
if(_.isEmpty(tags)) {
tags = processStyleTags(delim[0].substring(2));
endTags = delim[0].length;
}
if(delim[0].startsWith('{{')) {
@@ -141,7 +142,14 @@ const mustacheSpans = {
}
},
renderer(token) {
return `<span class="inline-block${token.tags}>${this.parser.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
const tags = token.tags;
tags.classes = ['inline-block', tags.classes].join(' ').trim();
return `<span` +
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
`${tags.id ? ` id="${tags.id}"` : ''}` +
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
`${tags.attributes ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
`>${this.parser.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
}
};
@@ -156,13 +164,13 @@ const mustacheDivs = {
if(match) {
//Find closing delimiter
let blockCount = 0;
let tags = '';
let tags = {};
let endTags = 0;
let endToken = 0;
let delim;
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
if(!tags) {
tags = `${processStyleTags(delim.substring(2))}`;
if(_.isEmpty(tags)) {
tags = processStyleTags(delim.substring(2));
endTags = delim.length + src.indexOf(delim);
}
if(delim.startsWith('{{')) {
@@ -190,7 +198,14 @@ const mustacheDivs = {
}
},
renderer(token) {
return `<div class="block${token.tags}>${this.parser.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML
const tags = token.tags;
tags.classes = ['block', tags.classes].join(' ').trim();
return `<div` +
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
`${tags.id ? ` id="${tags.id}"` : ''}` +
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
`${tags.attributes ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
`>${this.parser.parse(token.tokens)}</div>`; // parse to turn child tokens into HTML
}
};
@@ -206,23 +221,39 @@ const mustacheInjectInline = {
if(!lastToken || lastToken.type == 'mustacheInjectInline')
return false;
const tags = `${processStyleTags(match[1])}`;
const tags = processStyleTags(match[1]);
lastToken.originalType = lastToken.type;
lastToken.type = 'mustacheInjectInline';
lastToken.tags = tags;
lastToken.injectedTags = tags;
return {
type : 'text', // Should match "name" above
type : 'mustacheInjectInline', // Should match "name" above
raw : match[0], // Text to consume from the source
text : ''
};
}
},
renderer(token) {
if(!token.originalType){
return;
}
token.type = token.originalType;
const text = this.parser.parseInline([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
const originalTags = extractHTMLStyleTags(text);
const injectedTags = token.injectedTags;
const tags = {
id : injectedTags.id || originalTags.id || null,
classes : [originalTags.classes, injectedTags.classes].join(' ').trim() || null,
styles : [originalTags.styles, injectedTags.styles].join(' ').trim() || null,
attributes : Object.assign(originalTags.attributes ?? {}, injectedTags.attributes ?? {})
};
const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text);
if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
return `${openingTag[1]}` +
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
`${tags.id ? ` id="${tags.id}"` : ''}` +
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
`${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
`${openingTag[2]}`; // parse to turn child tokens into HTML
}
return text;
}
@@ -242,7 +273,7 @@ const mustacheInjectBlock = {
return false;
lastToken.originalType = 'mustacheInjectBlock';
lastToken.tags = `${processStyleTags(match[1])}`;
lastToken.injectedTags = processStyleTags(match[1]);
return {
type : 'mustacheInjectBlock', // Should match "name" above
raw : match[0], // Text to consume from the source
@@ -256,9 +287,22 @@ const mustacheInjectBlock = {
}
token.type = token.originalType;
const text = this.parser.parse([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
const originalTags = extractHTMLStyleTags(text);
const injectedTags = token.injectedTags;
const tags = {
id : injectedTags.id || originalTags.id || null,
classes : [originalTags.classes, injectedTags.classes].join(' ').trim() || null,
styles : [originalTags.styles, injectedTags.styles].join(' ').trim() || null,
attributes : Object.assign(originalTags.attributes ?? {}, injectedTags.attributes ?? {})
};
const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text);
if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
return `${openingTag[1]}` +
`${tags.classes ? ` class="${tags.classes}"` : ''}` +
`${tags.id ? ` id="${tags.id}"` : ''}` +
`${tags.styles ? ` style="${tags.styles}"` : ''}` +
`${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` +
`${openingTag[2]}`; // parse to turn child tokens into HTML
}
return text;
}
@@ -311,13 +355,13 @@ const definitionListsSingleLine = {
let endIndex = 0;
const definitions = [];
while (match = regex.exec(src)) {
let originalLine = match[0]; // This line and below to handle conflict with emojis
const originalLine = match[0]; // This line and below to handle conflict with emojis
let firstLine = originalLine; // Remove in V4 when definitionListsInline updated to
this.lexer.inlineTokens(firstLine.trim()) // require spaces around `::`
.filter(t => t.type == 'emoji')
.map(emoji => firstLine = firstLine.replace(emoji.raw, 'x'.repeat(emoji.raw.length)));
.filter((t)=>t.type == 'emoji')
.map((emoji)=>firstLine = firstLine.replace(emoji.raw, 'x'.repeat(emoji.raw.length)));
let newMatch = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym.exec(firstLine);
const newMatch = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym.exec(firstLine);
if(newMatch) {
definitions.push({
dt : this.lexer.inlineTokens(originalLine.slice(0, newMatch[1].length).trim()),
@@ -633,15 +677,22 @@ function MarkedVariables() {
//^=====--------------------< Variable Handling >-------------------=====^//
// Emoji options
// To add more icon fonts, need to do these things
// 1) Add the font file as .woff2 to themes/fonts/iconFonts folder
// 2) Create a .less file mapping CSS class names to the font character
// 3) Create a .js file mapping Autosuggest names to CSS class names
// 4) Import the .less file into shared/naturalcrit/codeEditor/codeEditor.less
// 5) Import the .less file into themes/V3/blank.style.less
// 6) Import the .js file to shared/naturalcrit/codeEditor/autocompleteEmoji.js and add to `emojis` object
// 7) Import the .js file here to markdown.js, and add to `emojis` object below
const MarkedEmojiOptions = {
emojis: {
emojis : {
...diceFont,
...elderberryInn,
...fontAwesome
...raRedux,
"fas-heart": "fa-solid fa-heart",
"fas-star": "fa-solid fa-star"
},
renderer: (token) => `<i class="${token.emoji}"></i>`
renderer : (token)=>`<i class="${token.emoji}"></i>`
};
Marked.use(MarkedVariables());
@@ -715,15 +766,45 @@ const processStyleTags = (string)=>{
//TODO: can we simplify to just split on commas?
const tags = string.match(/(?:[^, ":=]+|[:=](?:"[^"]*"|))+/g);
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0];
const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('=')));
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'));
const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()) : [];
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0] || null;
const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('='))).join(' ') || null;
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'))
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
.reduce((obj, attr)=>{
let [key, value] = attr.split('=');
value = value.replace(/"/g, '');
obj[key] = value;
return obj;
}, {}) || null;
const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()).join(' ') : null;
return `${classes?.length ? ` ${classes.join(' ')}` : ''}"` +
`${id ? ` id="${id}"` : ''}` +
`${styles?.length ? ` style="${styles.join(' ')}"` : ''}` +
`${attributes?.length ? ` ${attributes.join(' ')}` : ''}`;
return {
id : id,
classes : classes,
styles : styles,
attributes : _.isEmpty(attributes) ? null : attributes
};
};
const extractHTMLStyleTags = (htmlString)=>{
const id = htmlString.match(/id="([^"]*)"/)?.[1] || null;
const classes = htmlString.match(/class="([^"]*)"/)?.[1] || null;
const styles = htmlString.match(/style="([^"]*)"/)?.[1] || null;
const attributes = htmlString.match(/[a-zA-Z]+="[^"]*"/g)
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
.reduce((obj, attr)=>{
let [key, value] = attr.split('=');
value = value.replace(/"/g, '');
obj[key] = value;
return obj;
}, {}) || null;
return {
id : id,
classes : classes,
styles : styles,
attributes : _.isEmpty(attributes) ? null : attributes
};
};
const globalVarsList = {};

View File

@@ -0,0 +1,58 @@
const Markdown = require('naturalcrit/markdown.js');
const dedent = require('dedent-tabs').default;
// Marked.js adds line returns after closing tags on some default tokens.
// This removes those line returns for comparison sake.
String.prototype.trimReturns = function(){
return this.replace(/\r?\n|\r/g, '');
};
const emoji = 'df_d12_2';
describe(`When emojis/icons are active`, ()=>{
it('when a word is between two colons (:word:), and a matching emoji exists, it is rendered as an emoji', function() {
const source = `:${emoji}:`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><i class="df d12-2"></i></p>`);
});
it('when a word is between two colons (:word:), and no matching emoji exists, it is not parsed', function() {
const source = `:invalid:`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>:invalid:</p>`);
});
it('two valid emojis with no whitespace are prioritized over definition lists', function() {
const source = `:${emoji}::${emoji}:`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><i class="df d12-2"></i><i class="df d12-2"></i></p>`);
});
it('definition lists that are not also part of an emoji can coexist with normal emojis', function() {
const source = `definition :: term ${emoji}::${emoji}:`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<dl><dt>definition</dt><dd>term df_d12_2:<i class="df d12-2"></i></dd></dl>`);
});
it('A valid emoji is compatible with curly injectors', function() {
const source = `:${emoji}:{color:blue,myClass}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><i class="df d12-2 myClass" style="color:blue;"></i></p>`);
});
it('Emojis are not parsed inside of curly span CSS blocks', function() {
const source = `{{color:${emoji} text}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<span class="inline-block" style="color:df_d12_2;">text</span>`);
});
it('Emojis are not parsed inside of curly div CSS blocks', function() {
const source = dedent`{{color:${emoji}
text
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:df_d12_2;"><p>text</p></div>`);
});
// another test of the editor to confirm an autocomplete menu opens
});

View File

@@ -130,8 +130,8 @@ describe('Inline: When using the Inline syntax {{ }}', ()=>{
describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
it('Renders a div with text only', function() {
const source = dedent`{{
text
}}`;
text
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"><p>text</p></div>`);
});
@@ -139,14 +139,14 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
it('Renders an empty div', function() {
const source = dedent`{{
}}`;
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block"></div>`);
});
it('Renders a single paragraph with opening and closing brackets', function() {
const source = dedent`{{
}}`;
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>{{}}</p>`);
});
@@ -154,79 +154,79 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
it('Renders a div with a single class', function() {
const source = dedent`{{cat
}}`;
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"></div>`);
});
it('Renders a div with a single class and text', function() {
const source = dedent`{{cat
Sample text.
}}`;
Sample text.
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat"><p>Sample text.</p></div>`);
});
it('Renders a div with two classes and text', function() {
const source = dedent`{{cat,dog
Sample text.
}}`;
Sample text.
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat dog"><p>Sample text.</p></div>`);
});
it('Renders a div with a style and text', function() {
const source = dedent`{{color:red
Sample text.
}}`;
Sample text.
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red;"><p>Sample text.</p></div>`);
});
it('Renders a div with a style that has a string variable, and text', function() {
const source = dedent`{{--stringVariable:"'string'"
Sample text.
}}`;
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.
}}`;
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
Sample text.
}}`;
Sample text.
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" style="color:red;"><p>Sample text.</p></div>`);
});
it('Renders a div with an ID, class, style and text (different order)', function() {
const source = dedent`{{color:red,cat,#dog
Sample text.
}}`;
Sample text.
}}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block cat" id="dog" style="color:red;"><p>Sample text.</p></div>`);
});
it('Renders a div with a single ID', function() {
const source = dedent`{{#cat,#dog
Sample text.
}}`;
Sample text.
}}`;
const rendered = Markdown.render(source).trimReturns();
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.
}}`;
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>`);
});
@@ -243,61 +243,91 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{
describe('Injection: When an injection tag follows an element', ()=>{
// FIXME: Most of these fail because injections currently replace attributes, rather than append to. Or just minor extra whitespace issues.
describe('and that element is an inline-block', ()=>{
it.failing('Renders a span "text" with no injection', function() {
it('Renders a span "text" with no injection', function() {
const source = '{{ text}}{}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block">text</span>');
});
it.failing('Renders a span "text" with injected Class name', function() {
it('Renders a span "text" with injected Class name', function() {
const source = '{{ text}}{ClassName}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block ClassName">text</span>');
});
it.failing('Renders a span "text" with injected attribute', function() {
it('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>');
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" a="b and c">text</span>');
});
it.failing('Renders a span "text" with injected style', function() {
it('Renders a span "text" with injected style', function() {
const source = '{{ text}}{color:red}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red;">text</span>');
});
it.failing('Renders a span "text" with injected style using a string variable', function() {
it('Renders a span "text" with injected style using a string variable', function() {
const source = `{{ text}}{--stringVariable:"'string'"}`;
const 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('Renders a span "text" with two injected styles', function() {
const source = '{{ text}}{color:red,background:blue}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:red; background:blue;">text</span>');
});
it.failing('Renders an emphasis element with injected Class name', function() {
it('Renders a span "text" with its own ID, overwritten with an injected ID', function() {
const source = '{{#oldId text}}{#newId}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" id="newId">text</span>');
});
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, plus a new one', function() {
const source = '{{attrA="old",attrB="old" text}}{attrA="new",attrC="new"}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" attrA="new" attrB="old" attrC="new">text</span>');
});
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, ignoring "class", "style", and "id"', function() {
const source = '{{attrA="old",attrB="old" text}}{attrA="new",attrC="new",class="new",style="new",id="new"}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" attrA="new" attrB="old" attrC="new">text</span>');
});
it('Renders a span "text" with its own styles, appended with injected styles', function() {
const source = '{{color:blue,height:10px text}}{width:10px,color:red}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block" style="color:blue; height:10px; width:10px; color:red;">text</span>');
});
it('Renders a span "text" with its own classes, appended with injected classes', function() {
const source = '{{classA,classB text}}{classA,classC}';
const rendered = Markdown.render(source);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<span class="inline-block classA classB classA classC">text</span>');
});
it('Renders an emphasis element with injected Class name', function() {
const source = '*emphasis*{big}';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><em class="big">emphasis</em></p>');
});
it.failing('Renders a code element with injected style', function() {
it('Renders a code element with injected style', function() {
const source = '`code`{background:gray}';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><code style="background:gray;">code</code></p>');
});
it.failing('Renders an image element with injected style', function() {
it('Renders an image element with injected style', function() {
const source = '![alt text](http://i.imgur.com/hMna6G0.png){position:absolute}';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><img src="http://i.imgur.com/hMna6G0.png" alt="homebrew mug" style="position:absolute;"></p>');
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><img style="position:absolute;" src="http://i.imgur.com/hMna6G0.png" alt="alt text"></p>');
});
it.failing('Renders an element modified by only the first of two consecutive injections', function() {
it('Renders an element modified by only the first of two consecutive injections', function() {
const source = '{{ text}}{color:red}{background:blue}';
const 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>');
@@ -306,61 +336,106 @@ describe('Injection: When an injection tag follows an element', ()=>{
it('Renders an image with added attributes', function() {
const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png) {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>`);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="position:absolute; bottom:20px; left:130px; width:220px;" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug" a="b and c" d="e"></p>`);
});
});
describe('and that element is a block', ()=>{
it.failing('renders a div "text" with no injection', function() {
it('renders a div "text" with no injection', function() {
const source = '{{\ntext\n}}\n{}';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block"><p>text</p></div>');
});
it.failing('renders a div "text" with injected Class name', function() {
it('renders a div "text" with injected Class name', function() {
const source = '{{\ntext\n}}\n{ClassName}';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block ClassName"><p>text</p></div>');
});
it.failing('renders a div "text" with injected style', function() {
it('renders a div "text" with injected style', function() {
const source = '{{\ntext\n}}\n{color:red}';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" style="color:red;"><p>text</p></div>');
});
it.failing('renders a div "text" with two injected styles', function() {
it('renders a div "text" with two injected styles', function() {
const source = dedent`{{
text
}}
{color:red,background:blue}`;
text
}}
{color:red,background:blue}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red; background:blue"><p>text</p></div>`);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="color:red; background:blue;"><p>text</p></div>`);
});
it.failing('renders a div "text" with injected variable string', function() {
it('renders a div "text" with injected variable string', function() {
const source = dedent`{{
text
}}
{--stringVariable:"'string'"}`;
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>`);
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class="block" style="--stringVariable:'string';"><p>text</p></div>`);
});
it.failing('renders an h2 header "text" with injected class name', function() {
it('Renders a span "text" with its own ID, overwritten with an injected ID', function() {
const source = dedent`{{#oldId
text
}}
{#newId}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" id="newId"><p>text</p></div>');
});
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, plus a new one', function() {
const source = dedent`{{attrA="old",attrB="old"
text
}}
{attrA="new",attrC="new"}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" attrA="new" attrB="old" attrC="new"><p>text</p></div>');
});
it('Renders a span "text" with its own attributes, overwritten with an injected attribute, ignoring "class", "style", and "id"', function() {
const source = dedent`{{attrA="old",attrB="old"
text
}}
{attrA="new",attrC="new",class="new",style="new",id="new"}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" attrA="new" attrB="old" attrC="new"><p>text</p></div>');
});
it('Renders a span "text" with its own styles, appended with injected styles', function() {
const source = dedent`{{color:blue,height:10px
text
}}
{width:10px,color:red}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block" style="color:blue; height:10px; width:10px; color:red;"><p>text</p></div>');
});
it('Renders a span "text" with its own classes, appended with injected classes', function() {
const source = dedent`{{classA,classB
text
}}
{classA,classC}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block classA classB classA classC"><p>text</p></div>');
});
it('renders an h2 header "text" with injected class name', function() {
const source = dedent`## text
{ClassName}`;
{ClassName}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName">text</h2>');
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName" id="text">text</h2>');
});
it.failing('renders a table with injected class name', function() {
it('renders a table with injected class name', function() {
const source = dedent`| Experience Points | Level |
|:------------------|:-----:|
| 0 | 1 |
| 300 | 2 |
|:------------------|:-----:|
| 0 | 1 |
| 300 | 2 |
{ClassName}`;
{ClassName}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<table class="ClassName"><thead><tr><th align=left>Experience Points</th><th align=center>Level</th></tr></thead><tbody><tr><td align=left>0</td><td align=center>1</td></tr><tr><td align=left>300</td><td align=center>2</td></tr></tbody></table>`);
});
@@ -376,23 +451,23 @@ describe('Injection: When an injection tag follows an element', ()=>{
// expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`...`); // FIXME: expect this to be injected into <ul>? Currently injects into last <li>
// });
it.failing('renders an h2 header "text" with injected class name, and "secondInjection" as regular text on the next line.', function() {
it('renders an h2 header "text" with injected class name, and "secondInjection" as regular text on the next line.', function() {
const source = dedent`## text
{ClassName}
{secondInjection}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName">text</h2><p>{secondInjection}</p>');
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<h2 class="ClassName" id="text">text</h2><p>{secondInjection}</p>');
});
it.failing('renders a div nested into another div, the inner with class=innerDiv and the other class=outerDiv', function() {
it('renders a div nested into another div, the inner with class=innerDiv and the other class=outerDiv', function() {
const source = dedent`{{
outer text
{{
inner text
}}
{innerDiv}
}}
{outerDiv}`;
outer text
{{
inner text
}}
{innerDiv}
}}
{outerDiv}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<div class="block outerDiv"><p>outer text</p><div class="block innerDiv"><p>inner text</p></div></div>');
});

View File

@@ -329,7 +329,7 @@ describe('Normal Links and Images', ()=>{
const source = `![alt text](url){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());
<p><img style="width:100px;" src="url" alt="alt text"></p>`.trimReturns());
});
it('Renders normal links', function() {

View File

@@ -1,6 +1,4 @@
@import (less) './themes/assets/assets.less';
@import (less) './themes/fonts/icon fonts/elderberryInn.less';
@import (less) './themes/fonts/icon fonts/diceFont.less';
:root {
//Colors
@@ -543,10 +541,8 @@
color : white;
text-shadow : unset;
text-transform : uppercase;
filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
-webkit-text-stroke: 0.2cm black;
paint-order:stroke;
}
h2 {
font-family : 'NodestoCapsCondensed';
@@ -554,10 +550,8 @@
font-weight : normal;
color : white;
letter-spacing : 0.1cm;
filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
-webkit-text-stroke: 0.14cm black;
paint-order:stroke;
}
hr {
position : relative;
@@ -603,10 +597,8 @@
font-size : 0.496cm;
color : white;
text-align : center;
filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
-webkit-text-stroke: 0.1cm black;
paint-order:stroke;
}
.logo {
position : absolute;

View File

@@ -1,6 +1,7 @@
@import (less) './themes/fonts/5e/fonts.less';
@import (less) './themes/assets/assets.less';
@import (less) './themes/fonts/icon fonts/diceFont.less';
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
@import (less) './themes/fonts/iconFonts/diceFont.less';
:root {
//Colors

View File

@@ -1,96 +0,0 @@
const diceFont = {
"df-F" : "df F",
"df-F-minus" : "df F-minus",
"df-F-plus" : "df F-plus",
"df-F-zero" : "df F-zero",
"df-d10" : "df d10",
"df-d10-1" : "df d10-1",
"df-d10-10" : "df d10-10",
"df-d10-2" : "df d10-2",
"df-d10-3" : "df d10-3",
"df-d10-4" : "df d10-4",
"df-d10-5" : "df d10-5",
"df-d10-6" : "df d10-6",
"df-d10-7" : "df d10-7",
"df-d10-8" : "df d10-8",
"df-d10-9" : "df d10-9",
"df-d12" : "df d12",
"df-d12-1" : "df d12-1",
"df-d12-10" : "df d12-10",
"df-d12-11" : "df d12-11",
"df-d12-12" : "df d12-12",
"df-d12-2" : "df d12-2",
"df-d12-3" : "df d12-3",
"df-d12-4" : "df d12-4",
"df-d12-5" : "df d12-5",
"df-d12-6" : "df d12-6",
"df-d12-7" : "df d12-7",
"df-d12-8" : "df d12-8",
"df-d12-9" : "df d12-9",
"df-d2" : "df d2",
"df-d2-1" : "df d2-1",
"df-d2-2" : "df d2-2",
"df-d20" : "df d20",
"df-d20-1" : "df d20-1",
"df-d20-10" : "df d20-10",
"df-d20-11" : "df d20-11",
"df-d20-12" : "df d20-12",
"df-d20-13" : "df d20-13",
"df-d20-14" : "df d20-14",
"df-d20-15" : "df d20-15",
"df-d20-16" : "df d20-16",
"df-d20-17" : "df d20-17",
"df-d20-18" : "df d20-18",
"df-d20-19" : "df d20-19",
"df-d20-2" : "df d20-2",
"df-d20-20" : "df d20-20",
"df-d20-3" : "df d20-3",
"df-d20-4" : "df d20-4",
"df-d20-5" : "df d20-5",
"df-d20-6" : "df d20-6",
"df-d20-7" : "df d20-7",
"df-d20-8" : "df d20-8",
"df-d20-9" : "df d20-9",
"df-d4" : "df d4",
"df-d4-1" : "df d4-1",
"df-d4-2" : "df d4-2",
"df-d4-3" : "df d4-3",
"df-d4-4" : "df d4-4",
"df-d6" : "df d6",
"df-d6-1" : "df d6-1",
"df-d6-2" : "df d6-2",
"df-d6-3" : "df d6-3",
"df-d6-4" : "df d6-4",
"df-d6-5" : "df d6-5",
"df-d6-6" : "df d6-6",
"df-d8" : "df d8",
"df-d8-1" : "df d8-1",
"df-d8-2" : "df d8-2",
"df-d8-3" : "df d8-3",
"df-d8-4" : "df d8-4",
"df-d8-5" : "df d8-5",
"df-d8-6" : "df d8-6",
"df-d8-7" : "df d8-7",
"df-d8-8" : "df d8-8",
"df-dot-d6" : "df dot-d6",
"df-dot-d6-1" : "df dot-d6-1",
"df-dot-d6-2" : "df dot-d6-2",
"df-dot-d6-3" : "df dot-d6-3",
"df-dot-d6-4" : "df dot-d6-4",
"df-dot-d6-5" : "df dot-d6-5",
"df-dot-d6-6" : "df dot-d6-6",
"df-small-dot-d6-1" : "df small-dot-d6-1",
"df-small-dot-d6-2" : "df small-dot-d6-2",
"df-small-dot-d6-3" : "df small-dot-d6-3",
"df-small-dot-d6-4" : "df small-dot-d6-4",
"df-small-dot-d6-5" : "df small-dot-d6-5",
"df-small-dot-d6-6" : "df small-dot-d6-6",
"df-solid-small-dot-d6-1" : "df solid-small-dot-d6-1",
"df-solid-small-dot-d6-2" : "df solid-small-dot-d6-2",
"df-solid-small-dot-d6-3" : "df solid-small-dot-d6-3",
"df-solid-small-dot-d6-4" : "df solid-small-dot-d6-4",
"df-solid-small-dot-d6-5" : "df solid-small-dot-d6-5",
"df-solid-small-dot-d6-6" : "df solid-small-dot-d6-6"
}
module.exports = diceFont;

View File

@@ -1,208 +0,0 @@
const elderberryInn = {
"EiBook" : "ei book",
"EiScreen" : "ei screen",
/* Spell levels */
"EiSpell-0" : "ei spell-0",
"EiSpell-1" : "ei spell-1",
"EiSpell-2" : "ei spell-2",
"EiSpell-3" : "ei spell-3",
"EiSpell-4" : "ei spell-4",
"EiSpell-5" : "ei spell-5",
"EiSpell-6" : "ei spell-6",
"EiSpell-7" : "ei spell-7",
"EiSpell-8" : "ei spell-8",
"EiSpell-9" : "ei spell-9",
/* Damage types */
"EiAcid" : "ei acid",
"EiBludgeoning" : "ei bludgeoning",
"EiCold" : "ei cold",
"EiFire" : "ei fire",
"EiForce" : "ei force",
"EiLightning" : "ei lightning",
"EiNecrotic" : "ei necrotic",
"EiPiercing" : "ei piercing",
"EiPoison" : "ei poison",
"EiPsychic" : "ei psychic",
"EiRadiant" : "ei radiant",
"EiSlashing" : "ei slashing",
"EiThunder" : "ei thunder",
/* DnD Conditions */
"EiBlinded" : "ei blinded",
"EiCharmed" : "ei charmed",
"EiDeafened" : "ei deafened",
"EiExhaust1" : "ei exhaust-1",
"EiBlinded" : "ei blinded",
"EiExhaust2" : "ei exhaust-2",
"EiExhaust3" : "ei exhaust-3",
"EiExhaust4" : "ei exhaust-4",
"EiExhaust5" : "ei exhaust-5",
"EiExhaust6" : "ei exhaust-6",
"EiFrightened" : "ei frightened",
"EiGrappled" : "ei grappled",
"EiIncapacitated" : "ei incapacitated",
"EiInvisible" : "ei invisible",
"EiParalyzed" : "ei paralyzed",
"EiPetrified" : "ei petrified",
"EiPoisoned" : "ei poisoned",
"EiProne" : "ei prone",
"EiRestrained" : "ei restrained",
"EiStunned" : "ei stunned",
"EiUnconscious" : "ei unconscious",
/* Character Classes and Features */
"EiBarbarianRage" : "ei barbarian-rage",
"EiBarbarianRecklessAttack" : "ei barbarian-reckless-attack",
"EiBardicInspiration" : "ei bardic-inspiration",
"EiClericChannelDivinity" : "ei cleric-channel-divinity",
"EiDruidWildShape" : "ei druid-wild-shape",
"EiFighterActionSurge" : "ei fighter-action-surge",
"EiFighterSecondWind" : "ei fighter-second-wind",
"EiMonkFlurryBlows" : "ei monk-flurry-blows",
"EiMonkPatientDefense" : "ei monk-patient-defense",
"EiMonkStepOfTheWind" : "ei monk-step-of-the-wind",
"EiMonkStepOfTheWind2" : "ei monk-step-of-the-wind-2",
"EiMonkStepOfTheWind3" : "ei monk-step-of-the-wind-3",
"EiMonkStunningStrike" : "ei monk-stunning-strike",
"EiMonkStunningStrike2" : "ei monk-stunning-strike-2",
"EiPaladinDivineSmite" : "ei paladin-divine-smite",
"EiPaladinLayOnHands" : "ei paladin-lay-on-hands",
"EiBarbarianAbilities" : "ei barbarian-abilities",
"EiBarbarian" : "ei barbarian",
"EiBardAbilities" : "ei bard-abilities",
"EiBard" : "ei bard",
"EiClericAbilities" : "ei cleric-abilities",
"EiCleric" : "ei cleric",
"EiDruidAbilities" : "ei druid-abilities",
"EiDruid" : "ei druid",
"EiFighterAbilities" : "ei fighter-abilities",
"EiFighter" : "ei fighter",
"EiMonkAbilities" : "ei monk-abilities",
"EiMonk" : "ei monk",
"EiPaladinAbilities" : "ei paladin-abilities",
"EiPaladin" : "ei paladin",
"EiRangerAbilities" : "ei ranger-abilities",
"EiRanger" : "ei ranger",
"EiRogueAbilities" : "ei rogue-abilities",
"EiRogue" : "ei rogue",
"EiSorcererAbilities" : "ei sorcerer-abilities",
"EiSorcerer" : "ei sorcerer",
"EiWarlockAbilities" : "ei warlock-abilities",
"EiWarlock" : "ei warlock",
"EiWizardAbilities" : "ei wizard-abilities",
"EiWizard" : "ei wizard",
/* Types of actions */
"EiMovement" : "ei movement",
"EiAction" : "ei action",
"EiBonusAction" : "ei bonus-action",
"EiReaction" : "ei reaction",
/* SRD Spells */
"EiAcidArrow" : "ei acid-arrow",
"EiAction1" : "ei action-1",
"EiAlterSelf" : "ei alter-self",
"EiAlterSelf2" : "ei alter-self-2",
"EiAnimalFriendship" : "ei animal-friendship",
"EiAnimateDead" : "ei animate-dead",
"EiAnimateObjects" : "ei animate-objects",
"EiAnimateObjects2" : "ei animate-objects-2",
"EiBane" : "ei bane",
"EiBless" : "ei bless",
"EiBlur" : "ei blur",
"EiBonus" : "ei bonus",
"EiBrandingSmite" : "ei branding-smite",
"EiBurningHands" : "ei burning-hands",
"EiCharmPerson" : "ei charm-person",
"EiChillTouch" : "ei chill-touch",
"EiCloudkill" : "ei cloudkill",
"EiComprehendLanguages" : "ei comprehend-languages",
"EiConeOfCold" : "ei cone-of-cold",
"EiConjureElemental" : "ei conjure-elemental",
"EiConjureMinorElemental" : "ei conjure-minor-elemental",
"EiControlWater" : "ei control-water",
"EiCounterspell" : "ei counterspell",
"EiCureWounds" : "ei cure-wounds",
"EiDancingLights" : "ei dancing-lights",
"EiDarkness" : "ei darkness",
"EiDetectMagic" : "ei detect-magic",
"EiDisguiseSelf" : "ei disguise-self",
"EiDisintegrate" : "ei disintegrate",
"EiDispelEvilAndGood" : "ei dispel-evil-and-good",
"EiDispelMagic" : "ei dispel-magic",
"EiDominateMonster" : "ei dominate-monster",
"EiDominatePerson" : "ei dominate-person",
"EiEldritchBlast" : "ei eldritch-blast",
"EiEnlargeReduce" : "ei enlarge-reduce",
"EiEntangle" : "ei entangle",
"EiFaerieFire" : "ei faerie-fire",
"EiFaerieFire2" : "ei faerie-fire2",
"EiFeatherFall" : "ei feather-fall",
"EiFindFamiliar" : "ei find-familiar",
"EiFingerOfDeath" : "ei finger-of-death",
"EiFireball" : "ei fireball",
"EiFloatingDisk" : "ei floating-disk",
"EiFly" : "ei fly",
"EiFogCloud" : "ei fog-cloud",
"EiGaseousForm" : "ei gaseous-form",
"EiGaseousForm2" : "ei gaseous-form2",
"EiGentleRepose" : "ei gentle-repose",
"EiGentleRepose2" : "ei gentle-repose2",
"EiGlobeOfInvulnerability" : "ei globe-of-invulnerability",
"EiGuidingBolt" : "ei guiding-bolt",
"EiHealingWord" : "ei healing-word",
"EiHeatMetal" : "ei heat-metal",
"EiHellishRebuke" : "ei hellish-rebuke",
"EiHeroesFeast" : "ei heroes-feast",
"EiHeroism" : "ei heroism",
"EiHideousLaughter" : "ei hideous-laughter",
"EiIdentify" : "ei identify",
"EiIllusoryScript" : "ei illusory-script",
"EiInflictWounds" : "ei inflict-wounds",
"EiLight" : "ei light",
"EiLongstrider" : "ei longstrider",
"EiMageArmor" : "ei mage-armor",
"EiMageHand" : "ei mage-hand",
"EiMagicMissile" : "ei magic-missile",
"EiMassCureWounds" : "ei mass-cure-wounds",
"EiMassHealingWord" : "ei mass-healing-word",
"EiMending" : "ei Mending",
"EiMessage" : "ei message",
"EiMinorIllusion" : "ei Minor-illusion",
"EiMovement1" : "ei movement1",
"EiPolymorph" : "ei polymorph",
"EiPowerWordKill" : "ei power-word-kill",
"EiPowerWordStun" : "ei power-word-stun",
"EiPrayerOfHealing" : "ei prayer-of-healing",
"EiPrestidigitation" : "ei prestidigitation",
"EiProtectionFromEvilAndGood" : "ei protection-from-evil-and-good",
"EiRaiseDead" : "ei raise-dead",
"EiRaiseDead2" : "ei raise-dead2",
"EiReaction1" : "ei reaction1",
"EiResurrection" : "ei resurrection",
"EiResurrection2" : "ei resurrection2",
"EiRevivify" : "ei revivify",
"EiRevivify2" : "ei revivify2",
"EiSacredFlame" : "ei sacred-flame",
"EiSanctuary" : "ei sanctuary",
"EiScorchingRay" : "ei scorching-ray",
"EiSending" : "ei sending",
"EiShatter" : "ei shatter",
"EiShield" : "ei shield",
"EiSilentImage" : "ei silent-image",
"EiSleep" : "ei sleep",
"EiSpeakWithAnimals" : "ei speak-with-animals",
"EiTelekinesis" : "ei telekinesis",
"EiTrueStrike" : "ei true-strike",
"EiViciousMockery" : "ei vicious-mockery",
"EiWallOfFire" : "ei wall-of-fire",
"EiWallOfForce" : "ei wall-of-force",
"EiWallOfIce" : "ei wall-of-ice",
"EiWallOfStone" : "ei wall-of-stone",
"EiWallOfThorns" : "ei wall-of-thorns",
"EiWish" : "ei wish"
}
module.exports = elderberryInn;

View File

@@ -0,0 +1,96 @@
const diceFont = {
'df_f' : 'df F',
'df_f_minus' : 'df F-minus',
'df_f_plus' : 'df F-plus',
'df_f_zero' : 'df F-zero',
'df_d10' : 'df d10',
'df_d10_1' : 'df d10-1',
'df_d10_10' : 'df d10-10',
'df_d10_2' : 'df d10-2',
'df_d10_3' : 'df d10-3',
'df_d10_4' : 'df d10-4',
'df_d10_5' : 'df d10-5',
'df_d10_6' : 'df d10-6',
'df_d10_7' : 'df d10-7',
'df_d10_8' : 'df d10-8',
'df_d10_9' : 'df d10-9',
'df_d12' : 'df d12',
'df_d12_1' : 'df d12-1',
'df_d12_10' : 'df d12-10',
'df_d12_11' : 'df d12-11',
'df_d12_12' : 'df d12-12',
'df_d12_2' : 'df d12-2',
'df_d12_3' : 'df d12-3',
'df_d12_4' : 'df d12-4',
'df_d12_5' : 'df d12-5',
'df_d12_6' : 'df d12-6',
'df_d12_7' : 'df d12-7',
'df_d12_8' : 'df d12-8',
'df_d12_9' : 'df d12-9',
'df_d2' : 'df d2',
'df_d2_1' : 'df d2-1',
'df_d2_2' : 'df d2-2',
'df_d20' : 'df d20',
'df_d20_1' : 'df d20-1',
'df_d20_10' : 'df d20-10',
'df_d20_11' : 'df d20-11',
'df_d20_12' : 'df d20-12',
'df_d20_13' : 'df d20-13',
'df_d20_14' : 'df d20-14',
'df_d20_15' : 'df d20-15',
'df_d20_16' : 'df d20-16',
'df_d20_17' : 'df d20-17',
'df_d20_18' : 'df d20-18',
'df_d20_19' : 'df d20-19',
'df_d20_2' : 'df d20-2',
'df_d20_20' : 'df d20-20',
'df_d20_3' : 'df d20-3',
'df_d20_4' : 'df d20-4',
'df_d20_5' : 'df d20-5',
'df_d20_6' : 'df d20-6',
'df_d20_7' : 'df d20-7',
'df_d20_8' : 'df d20-8',
'df_d20_9' : 'df d20-9',
'df_d4' : 'df d4',
'df_d4_1' : 'df d4-1',
'df_d4_2' : 'df d4-2',
'df_d4_3' : 'df d4-3',
'df_d4_4' : 'df d4-4',
'df_d6' : 'df d6',
'df_d6_1' : 'df d6-1',
'df_d6_2' : 'df d6-2',
'df_d6_3' : 'df d6-3',
'df_d6_4' : 'df d6-4',
'df_d6_5' : 'df d6-5',
'df_d6_6' : 'df d6-6',
'df_d8' : 'df d8',
'df_d8_1' : 'df d8-1',
'df_d8_2' : 'df d8-2',
'df_d8_3' : 'df d8-3',
'df_d8_4' : 'df d8-4',
'df_d8_5' : 'df d8-5',
'df_d8_6' : 'df d8-6',
'df_d8_7' : 'df d8-7',
'df_d8_8' : 'df d8-8',
'df_dot_d6' : 'df dot-d6',
'df_dot_d6_1' : 'df dot-d6-1',
'df_dot_d6_2' : 'df dot-d6-2',
'df_dot_d6_3' : 'df dot-d6-3',
'df_dot_d6_4' : 'df dot-d6-4',
'df_dot_d6_5' : 'df dot-d6-5',
'df_dot_d6_6' : 'df dot-d6-6',
'df_small_dot_d6_1' : 'df small-dot-d6-1',
'df_small_dot_d6_2' : 'df small-dot-d6-2',
'df_small_dot_d6_3' : 'df small-dot-d6-3',
'df_small_dot_d6_4' : 'df small-dot-d6-4',
'df_small_dot_d6_5' : 'df small-dot-d6-5',
'df_small_dot_d6_6' : 'df small-dot-d6-6',
'df_solid_small_dot_d6_1' : 'df solid-small-dot-d6-1',
'df_solid_small_dot_d6_2' : 'df solid-small-dot-d6-2',
'df_solid_small_dot_d6_3' : 'df solid-small-dot-d6-3',
'df_solid_small_dot_d6_4' : 'df solid-small-dot-d6-4',
'df_solid_small_dot_d6_5' : 'df solid-small-dot-d6-5',
'df_solid_small_dot_d6_6' : 'df solid-small-dot-d6-6'
};
module.exports = diceFont;

View File

@@ -3,7 +3,7 @@
font-family : 'DiceFont';
font-style : normal;
font-weight : normal;
src : url('../../../fonts/icon fonts/diceFont.woff2');
src : url('../../../fonts/iconFonts/diceFont.woff2');
}
.df {

View File

@@ -0,0 +1,208 @@
const elderberryInn = {
'ei_book' : 'ei book',
'ei_screen' : 'ei screen',
/* Spell levels */
'ei_spell_0' : 'ei spell-0',
'ei_spell_1' : 'ei spell-1',
'ei_spell_2' : 'ei spell-2',
'ei_spell_3' : 'ei spell-3',
'ei_spell_4' : 'ei spell-4',
'ei_spell_5' : 'ei spell-5',
'ei_spell_6' : 'ei spell-6',
'ei_spell_7' : 'ei spell-7',
'ei_spell_8' : 'ei spell-8',
'ei_spell_9' : 'ei spell-9',
/* Damage types */
'ei_acid' : 'ei acid',
'ei_bludgeoning' : 'ei bludgeoning',
'ei_cold' : 'ei cold',
'ei_fire' : 'ei fire',
'ei_force' : 'ei force',
'ei_lightning' : 'ei lightning',
'ei_necrotic' : 'ei necrotic',
'ei_piercing' : 'ei piercing',
'ei_poison' : 'ei poison',
'ei_psychic' : 'ei psychic',
'ei_radiant' : 'ei radiant',
'ei_slashing' : 'ei slashing',
'ei_thunder' : 'ei thunder',
/* DnD Donditions */
'ei_blinded' : 'ei blinded',
'ei_charmed' : 'ei charmed',
'ei_deafened' : 'ei deafened',
'ei_exhaust1' : 'ei exhaust-1',
'ei_blinded' : 'ei blinded',
'ei_exhaust2' : 'ei exhaust-2',
'ei_exhaust3' : 'ei exhaust-3',
'ei_exhaust4' : 'ei exhaust-4',
'ei_exhaust5' : 'ei exhaust-5',
'ei_exhaust6' : 'ei exhaust-6',
'ei_frightened' : 'ei frightened',
'ei_grappled' : 'ei grappled',
'ei_incapacitated' : 'ei incapacitated',
'ei_invisible' : 'ei invisible',
'ei_paralyzed' : 'ei paralyzed',
'ei_petrified' : 'ei petrified',
'ei_poisoned' : 'ei poisoned',
'ei_prone' : 'ei prone',
'ei_restrained' : 'ei restrained',
'ei_stunned' : 'ei stunned',
'ei_unconscious' : 'ei unconscious',
/* Character Classes and Features */
'ei_barbarian_rage' : 'ei barbarian-rage',
'ei_barbarian_reckless_attack' : 'ei barbarian-reckless-attack',
'ei_bardic_inspiration' : 'ei bardic-inspiration',
'ei_cleric_channel_divinity' : 'ei cleric-channel-divinity',
'ei_druid_wild_shape' : 'ei druid-wild-shape',
'ei_fighter_action_surge' : 'ei fighter-action-surge',
'ei_fighter_second_wind' : 'ei fighter-second-wind',
'ei_monk_flurry_blows' : 'ei monk-flurry-blows',
'ei_monk_patient_defense' : 'ei monk-patient-defense',
'ei_monk_step_of_the_wind' : 'ei monk-step-of-the-wind',
'ei_monk_step_of_the_wind2' : 'ei monk-step-of-the-wind-2',
'ei_monk_step_of_the_wind3' : 'ei monk-step-of-the-wind-3',
'ei_monk_stunning_strike' : 'ei monk-stunning-strike',
'ei_monk_stunning_strike2' : 'ei monk-stunning-strike-2',
'ei_paladin_divine_smite' : 'ei paladin-divine-smite',
'ei_paladin_lay_on_hands' : 'ei paladin-lay-on-hands',
'ei_barbarian_abilities' : 'ei barbarian-abilities',
'ei_barbarian' : 'ei barbarian',
'ei_bard_abilities' : 'ei bard-abilities',
'ei_bard' : 'ei bard',
'ei_cleric_abilities' : 'ei cleric-abilities',
'ei_cleric' : 'ei cleric',
'ei_druid_abilities' : 'ei druid-abilities',
'ei_druid' : 'ei druid',
'ei_fighter_abilities' : 'ei fighter-abilities',
'ei_fighter' : 'ei fighter',
'ei_monk_abilities' : 'ei monk-abilities',
'ei_monk' : 'ei monk',
'ei_paladin_abilities' : 'ei paladin-abilities',
'ei_paladin' : 'ei paladin',
'ei_ranger_abilities' : 'ei ranger-abilities',
'ei_ranger' : 'ei ranger',
'ei_rogue_abilities' : 'ei rogue-abilities',
'ei_rogue' : 'ei rogue',
'ei_sorcerer_abilities' : 'ei sorcerer-abilities',
'ei_sorcerer' : 'ei sorcerer',
'ei_warlock_abilities' : 'ei warlock-abilities',
'ei_warlock' : 'ei warlock',
'ei_wizard_abilities' : 'ei wizard-abilities',
'ei_wizard' : 'ei wizard',
/* Types of actions */
'ei_movement' : 'ei movement',
'ei_action' : 'ei action',
'ei_bonus_action' : 'ei bonus-action',
'ei_reaction' : 'ei reaction',
/* SRD Spells */
'ei_acid_arrow' : 'ei acid-arrow',
'ei_action1' : 'ei action-1',
'ei_alter_self' : 'ei alter-self',
'ei_alter_self2' : 'ei alter-self-2',
'ei_animal_friendship' : 'ei animal-friendship',
'ei_animate_dead' : 'ei animate-dead',
'ei_animate_objects' : 'ei animate-objects',
'ei_animate_objects2' : 'ei animate-objects-2',
'ei_bane' : 'ei bane',
'ei_bless' : 'ei bless',
'ei_blur' : 'ei blur',
'ei_bonus' : 'ei bonus',
'ei_branding_smite' : 'ei branding-smite',
'ei_burning_hands' : 'ei burning-hands',
'ei_charm_person' : 'ei charm-person',
'ei_chill_touch' : 'ei chill-touch',
'ei_cloudkill' : 'ei cloudkill',
'ei_comprehend_languages' : 'ei comprehend-languages',
'ei_cone_of_cold' : 'ei cone-of-cold',
'ei_conjure_elemental' : 'ei conjure-elemental',
'ei_conjure_minor_elemental' : 'ei conjure-minor-elemental',
'ei_control_water' : 'ei control-water',
'ei_counterspell' : 'ei counterspell',
'ei_cure_wounds' : 'ei cure-wounds',
'ei_dancing_lights' : 'ei dancing-lights',
'ei_darkness' : 'ei darkness',
'ei_detect_magic' : 'ei detect-magic',
'ei_disguise_self' : 'ei disguise-self',
'ei_disintegrate' : 'ei disintegrate',
'ei_dispel_evil_and_good' : 'ei dispel-evil-and-good',
'ei_dispel_magic' : 'ei dispel-magic',
'ei_dominate_monster' : 'ei dominate-monster',
'ei_dominate_person' : 'ei dominate-person',
'ei_eldritch_blast' : 'ei eldritch-blast',
'ei_enlarge_reduce' : 'ei enlarge-reduce',
'ei_entangle' : 'ei entangle',
'ei_faerie_fire' : 'ei faerie-fire',
'ei_faerie_fire2' : 'ei faerie-fire2',
'ei_feather_fall' : 'ei feather-fall',
'ei_find_familiar' : 'ei find-familiar',
'ei_finger_of_death' : 'ei finger-of-death',
'ei_fireball' : 'ei fireball',
'ei_floating_disk' : 'ei floating-disk',
'ei_fly' : 'ei fly',
'ei_fog_cloud' : 'ei fog-cloud',
'ei_gaseous_form' : 'ei gaseous-form',
'ei_gaseous_form2' : 'ei gaseous-form2',
'ei_gentle_repose' : 'ei gentle-repose',
'ei_gentle_repose2' : 'ei gentle-repose2',
'ei_globe_of_invulnerability' : 'ei globe-of-invulnerability',
'ei_guiding_bolt' : 'ei guiding-bolt',
'ei_healing_word' : 'ei healing-word',
'ei_heat_metal' : 'ei heat-metal',
'ei_hellish_rebuke' : 'ei hellish-rebuke',
'ei_heroes_feast' : 'ei heroes-feast',
'ei_heroism' : 'ei heroism',
'ei_hideous_laughter' : 'ei hideous-laughter',
'ei_identify' : 'ei identify',
'ei_illusory_script' : 'ei illusory-script',
'ei_inflict_wounds' : 'ei inflict-wounds',
'ei_light' : 'ei light',
'ei_longstrider' : 'ei longstrider',
'ei_mage_armor' : 'ei mage-armor',
'ei_mage_hand' : 'ei mage-hand',
'ei_magic_missile' : 'ei magic-missile',
'ei_mass_cure_wounds' : 'ei mass-cure-wounds',
'ei_mass_healing_word' : 'ei mass-healing-word',
'ei_mending' : 'ei _mending',
'ei_message' : 'ei message',
'ei_minor_illusion' : 'ei _minor-illusion',
'ei_movement1' : 'ei movement1',
'ei_polymorph' : 'ei polymorph',
'ei_power_word_kill' : 'ei power-word-kill',
'ei_power_word_stun' : 'ei power-word-stun',
'ei_prayer_of_healing' : 'ei prayer-of-healing',
'ei_prestidigitation' : 'ei prestidigitation',
'ei_protection_from_evil_and_good' : 'ei protection-from-evil-and-good',
'ei_raise_dead' : 'ei raise-dead',
'ei_raise_dead2' : 'ei raise-dead2',
'ei_reaction1' : 'ei reaction1',
'ei_resurrection' : 'ei resurrection',
'ei_resurrection2' : 'ei resurrection2',
'ei_revivify' : 'ei revivify',
'ei_revivify2' : 'ei revivify2',
'ei_sacred_flame' : 'ei sacred-flame',
'ei_sanctuary' : 'ei sanctuary',
'ei_scorching_ray' : 'ei scorching-ray',
'ei_sending' : 'ei sending',
'ei_shatter' : 'ei shatter',
'ei_shield' : 'ei shield',
'ei_silent_image' : 'ei silent-image',
'ei_sleep' : 'ei sleep',
'ei_speak_with_animals' : 'ei speak-with-animals',
'ei_telekinesis' : 'ei telekinesis',
'ei_true_strike' : 'ei true-strike',
'ei_vicious_mockery' : 'ei vicious-mockery',
'ei_wall_of_fire' : 'ei wall-of-fire',
'ei_wall_of_force' : 'ei wall-of-force',
'ei_wall_of_ice' : 'ei wall-of-ice',
'ei_wall_of_stone' : 'ei wall-of-stone',
'ei_wall_of_thorns' : 'ei wall-of-thorns',
'ei_wish' : 'ei wish'
};
module.exports = elderberryInn;

View File

@@ -3,7 +3,7 @@
font-family : 'Elderberry-Inn';
font-style : normal;
font-weight : normal;
src : url('../../../fonts/icon fonts/elderberryInn.woff2');
src : url('../../../fonts/iconFonts/elderberryInn.woff2');
}
.ei {
@@ -39,8 +39,8 @@
&.force::before { content : '\E910'; }
&.lightning::before { content : '\E911'; }
&.necrotic::before { content : '\E912'; }
&.piercing::before { content : '\E914'; }
&.poison::before { content : '\E913'; }
&.piercing::before { content : '\E913'; }
&.poison::before { content : '\E914'; }
&.psychic::before { content : '\E915'; }
&.radiant::before { content : '\E916'; }
&.slashing::before { content : '\E917'; }

File diff suppressed because it is too large Load Diff