mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-09-20 16:42:58 +00:00
Merge branch 'master' into experimentalMarkedDiagrams
This commit is contained in:
+10
-10
@@ -8,7 +8,7 @@ const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=nul
|
||||
const mpAsSnippets = [];
|
||||
// Snippets from Themes first.
|
||||
if(themeBundleSnippets) {
|
||||
for (let themes of themeBundleSnippets) {
|
||||
for (const themes of themeBundleSnippets) {
|
||||
if(typeof themes !== 'string') {
|
||||
const userSnippets = [];
|
||||
const snipSplit = themes.snippets.trim().split(textSplit).slice(1);
|
||||
@@ -76,9 +76,9 @@ const yamlSnippetsToText = (yamlObj)=>{
|
||||
if(typeof yamlObj == 'string') return yamlObj;
|
||||
|
||||
let snippetsText = '';
|
||||
|
||||
for (let snippet of yamlObj) {
|
||||
for (let subSnippet of snippet.subsnippets) {
|
||||
|
||||
for (const snippet of yamlObj) {
|
||||
for (const subSnippet of snippet.subsnippets) {
|
||||
snippetsText = `${snippetsText}\\snippet ${subSnippet.name}\n${subSnippet.gen || ''}\n`;
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ const fetchThemeBundle = async (setError, setThemeBundle, renderer, theme)=>{
|
||||
const res = await request
|
||||
.get(`/api/theme/${renderer}/${theme}`)
|
||||
.catch((err)=>{
|
||||
setError(err)
|
||||
setError(err);
|
||||
});
|
||||
if(!res) {
|
||||
setThemeBundle({});
|
||||
@@ -133,14 +133,14 @@ const fetchThemeBundle = async (setError, setThemeBundle, renderer, theme)=>{
|
||||
setError(null);
|
||||
};
|
||||
|
||||
const debugTextMismatch = (clientTextRaw, serverTextRaw, label) => {
|
||||
const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{
|
||||
const clientText = clientTextRaw?.normalize('NFC') || '';
|
||||
const serverText = serverTextRaw?.normalize('NFC') || '';
|
||||
|
||||
const clientBuffer = Buffer.from(clientText, 'utf8');
|
||||
const serverBuffer = Buffer.from(serverText, 'utf8');
|
||||
|
||||
if (clientBuffer.equals(serverBuffer)) {
|
||||
if(clientBuffer.equals(serverBuffer)) {
|
||||
console.log(`✅ ${label} text matches byte-for-byte.`);
|
||||
return;
|
||||
}
|
||||
@@ -151,7 +151,7 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label) => {
|
||||
|
||||
// Byte-level diff
|
||||
for (let i = 0; i < Math.min(clientBuffer.length, serverBuffer.length); i++) {
|
||||
if (clientBuffer[i] !== serverBuffer[i]) {
|
||||
if(clientBuffer[i] !== serverBuffer[i]) {
|
||||
console.log(`Byte mismatch at offset ${i}: client=0x${clientBuffer[i].toString(16)} server=0x${serverBuffer[i].toString(16)}`);
|
||||
break;
|
||||
}
|
||||
@@ -159,14 +159,14 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label) => {
|
||||
|
||||
// Char-level diff
|
||||
for (let i = 0; i < Math.min(clientText.length, serverText.length); i++) {
|
||||
if (clientText[i] !== serverText[i]) {
|
||||
if(clientText[i] !== serverText[i]) {
|
||||
console.log(`Char mismatch at index ${i}:`);
|
||||
console.log(` Client: '${clientText[i]}' (U+${clientText.charCodeAt(i).toString(16).toUpperCase()})`);
|
||||
console.log(` Server: '${serverText[i]}' (U+${serverText.charCodeAt(i).toString(16).toUpperCase()})`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
splitTextStyleAndMetadata,
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
require('./renderWarnings.less');
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
|
||||
import Dialog from '../../../client/components/dialog.jsx';
|
||||
|
||||
const RenderWarnings = createClass({
|
||||
displayName : 'RenderWarnings',
|
||||
getInitialState : function() {
|
||||
return {
|
||||
warnings : {}
|
||||
};
|
||||
},
|
||||
componentDidMount : function() {
|
||||
this.checkWarnings();
|
||||
window.addEventListener('resize', this.checkWarnings);
|
||||
},
|
||||
componentWillUnmount : function() {
|
||||
window.removeEventListener('resize', this.checkWarnings);
|
||||
},
|
||||
warnings : {
|
||||
chrome : function(){
|
||||
const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
|
||||
if(!isChrome){
|
||||
return <li key='chrome'>
|
||||
<em>Built for Chrome </em> <br />
|
||||
Other browsers have not been tested for compatiblilty. If you
|
||||
experience issues with your document not rendering or printing
|
||||
properly, please try using the latest version of Chrome before
|
||||
submitting a bug report.
|
||||
</li>;
|
||||
}
|
||||
},
|
||||
},
|
||||
checkWarnings : function(){
|
||||
this.setState({
|
||||
warnings : _.reduce(this.warnings, (r, fn, type)=>{
|
||||
const element = fn();
|
||||
if(element) r[type] = element;
|
||||
return r;
|
||||
}, {})
|
||||
});
|
||||
},
|
||||
render : function(){
|
||||
if(_.isEmpty(this.state.warnings)) return null;
|
||||
|
||||
const DISMISS_KEY = 'dismiss_render_warning';
|
||||
const DISMISS_TEXT = <i className='fas fa-times dismiss' />;
|
||||
|
||||
return <Dialog className='renderWarnings' dismissKey={DISMISS_KEY} closeText={DISMISS_TEXT}>
|
||||
<i className='fas fa-exclamation-triangle ohno' />
|
||||
<h3>Render Warnings</h3>
|
||||
<small>If this homebrew is rendering badly if might be because of the following:</small>
|
||||
<ul>{_.values(this.state.warnings)}</ul>
|
||||
</Dialog>;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = RenderWarnings;
|
||||
@@ -1,48 +0,0 @@
|
||||
.renderWarnings {
|
||||
position : relative;
|
||||
float : right;
|
||||
width : 350px;
|
||||
padding : 20px;
|
||||
padding-bottom : 10px;
|
||||
padding-left : 85px;
|
||||
margin-bottom : 10px;
|
||||
color : white;
|
||||
background-color : @yellow;
|
||||
border : none;
|
||||
a { font-weight : 800; }
|
||||
i.ohno {
|
||||
position : absolute;
|
||||
top : 24px;
|
||||
left : 24px;
|
||||
font-size : 2.5em;
|
||||
opacity : 0.8;
|
||||
}
|
||||
button.dismiss {
|
||||
position : absolute;
|
||||
top : 10px;
|
||||
right : 10px;
|
||||
cursor : pointer;
|
||||
background-color : transparent;
|
||||
opacity : 0.6;
|
||||
&:hover { opacity : 1; }
|
||||
}
|
||||
small {
|
||||
font-size : 0.6em;
|
||||
opacity : 0.7;
|
||||
}
|
||||
h3 {
|
||||
font-size : 1.1em;
|
||||
font-weight : 800;
|
||||
}
|
||||
ul {
|
||||
margin-top : 15px;
|
||||
font-size : 0.8em;
|
||||
list-style-position : outside;
|
||||
list-style-type : disc;
|
||||
li {
|
||||
font-size : 0.8em;
|
||||
line-height : 1.6em;
|
||||
em { font-weight : 800; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +1,31 @@
|
||||
/* eslint-disable max-depth */
|
||||
|
||||
/* eslint-disable max-lines */
|
||||
import _ from 'lodash';
|
||||
import { Parser as MathParser } from 'expr-eval';
|
||||
import { marked as Marked } from 'marked';
|
||||
import MarkedExtendedTables from 'marked-extended-tables';
|
||||
import MarkedDefinitionLists from 'marked-definition-lists';
|
||||
import MarkedAlignedParagraphs from 'marked-alignment-paragraphs';
|
||||
import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces';
|
||||
import MarkedSubSuperText from 'marked-subsuper-text';
|
||||
import { markedVariables,
|
||||
setMarkedVariablePage,
|
||||
setMarkedVariable,
|
||||
getMarkedVariable } from 'marked-variables';
|
||||
import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite';
|
||||
import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id';
|
||||
import { markedEmoji as MarkedEmojis } from 'marked-emoji';
|
||||
import MarkedDiagrams from 'marked-diagrams';
|
||||
import { romanize } from 'romans';
|
||||
import writtenNumber from 'written-number';
|
||||
|
||||
|
||||
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
||||
import diceFont from '../../themes/fonts/iconFonts/diceFont.js';
|
||||
import elderberryInn from '../../themes/fonts/iconFonts/elderberryInn.js';
|
||||
import gameIcons from '../../themes/fonts/iconFonts/gameIcons.js';
|
||||
import fontAwesome from '../../themes/fonts/iconFonts/fontAwesome.js';
|
||||
import diceFont from '../themes/fonts/iconFonts/diceFont.js';
|
||||
import elderberryInn from '../themes/fonts/iconFonts/elderberryInn.js';
|
||||
import gameIcons from '../themes/fonts/iconFonts/gameIcons.js';
|
||||
import fontAwesome from '../themes/fonts/iconFonts/fontAwesome.js';
|
||||
|
||||
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,
|
||||
abs : 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, trunc : false, join : false, sum : false, indexOf : 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,
|
||||
|
||||
remainder : false, factorial : false,
|
||||
comparison : false, concatenate : false,
|
||||
logical : false, assignment : false,
|
||||
array : false, fndef : false
|
||||
}
|
||||
});
|
||||
// Add sign function
|
||||
mathParser.functions.sign = function (a) {
|
||||
if(a >= 0) return '+';
|
||||
return '-';
|
||||
};
|
||||
// Add signed function
|
||||
mathParser.functions.signed = function (a) {
|
||||
if(a >= 0) return `+${a}`;
|
||||
return `${a}`;
|
||||
};
|
||||
// Add Roman numeral functions
|
||||
mathParser.functions.toRomans = function (a) {
|
||||
return romanize(a);
|
||||
};
|
||||
mathParser.functions.toRomansUpper = function (a) {
|
||||
return romanize(a).toUpperCase();
|
||||
};
|
||||
mathParser.functions.toRomansLower = function (a) {
|
||||
return romanize(a).toLowerCase();
|
||||
};
|
||||
// Add character functions
|
||||
mathParser.functions.toChar = function (a) {
|
||||
if(a <= 0) return a;
|
||||
const genChars = function (i) {
|
||||
return (i > 26 ? genChars(Math.floor((i - 1) / 26)) : '') + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[(i - 1) % 26];
|
||||
};
|
||||
return genChars(a);
|
||||
};
|
||||
mathParser.functions.toCharUpper = function (a) {
|
||||
return mathParser.functions.toChar(a).toUpperCase();
|
||||
};
|
||||
mathParser.functions.toCharLower = function (a) {
|
||||
return mathParser.functions.toChar(a).toLowerCase();
|
||||
};
|
||||
// Add word functions
|
||||
mathParser.functions.toWords = function (a) {
|
||||
return writtenNumber(a);
|
||||
};
|
||||
mathParser.functions.toWordsUpper = function (a) {
|
||||
return mathParser.functions.toWords(a).toUpperCase();
|
||||
};
|
||||
mathParser.functions.toWordsLower = function (a) {
|
||||
return mathParser.functions.toWords(a).toLowerCase();
|
||||
};
|
||||
mathParser.functions.toWordsCaps = function (a) {
|
||||
const words = mathParser.functions.toWords(a).split(' ');
|
||||
return words.map((word)=>{
|
||||
return word.replace(/(?:^|\b|\s)(\w)/g, function(w, index) {
|
||||
return index === 0 ? w.toLowerCase() : w.toUpperCase();
|
||||
});
|
||||
}).join(' ');
|
||||
};
|
||||
|
||||
// Normalize variable names; trim edge spaces and shorten blocks of whitespace to 1 space
|
||||
const normalizeVarNames = (label)=>{
|
||||
return label.trim().replace(/\s+/g, ' ');
|
||||
};
|
||||
|
||||
//Processes the markdown within an HTML block if it's just a class-wrapper
|
||||
renderer.html = function (token) {
|
||||
let html = token.text;
|
||||
@@ -120,7 +33,12 @@ renderer.html = function (token) {
|
||||
const openTag = html.substring(0, html.indexOf('>')+1);
|
||||
html = html.substring(html.indexOf('>')+1);
|
||||
html = html.substring(0, html.lastIndexOf('</div>'));
|
||||
return `${openTag} ${Marked.parse(html)} </div>`;
|
||||
|
||||
// Repeat the markdown processing for content inside the div, minus the preprocessing and postprocessing hooks which should only run once globally
|
||||
const opts = Marked.defaults;
|
||||
const tokens = Marked.lexer(html, opts);
|
||||
Marked.walkTokens(tokens, opts.walkTokens);
|
||||
return `${openTag} ${Marked.parser(tokens, opts)} </div>`;
|
||||
}
|
||||
return html;
|
||||
};
|
||||
@@ -186,7 +104,7 @@ const mustacheSpans = {
|
||||
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
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);
|
||||
if(match) {
|
||||
//Find closing delimiter
|
||||
@@ -243,7 +161,7 @@ const mustacheDivs = {
|
||||
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
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);
|
||||
if(match) {
|
||||
//Find closing delimiter
|
||||
@@ -298,7 +216,7 @@ const mustacheInjectInline = {
|
||||
level : 'inline',
|
||||
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?.&:!@$^;:\[\]_= ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
||||
const match = inlineRegex.exec(src);
|
||||
if(match) {
|
||||
const lastToken = tokens[tokens.length - 1];
|
||||
@@ -344,7 +262,7 @@ const mustacheInjectBlock = {
|
||||
level : 'block',
|
||||
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?.&:!@$^;:\[\]_= ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
||||
const match = inlineRegex.exec(src);
|
||||
if(match) {
|
||||
const lastToken = tokens[tokens.length - 1];
|
||||
@@ -412,248 +330,6 @@ const forcedParagraphBreaks = {
|
||||
}
|
||||
};
|
||||
|
||||
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
||||
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
||||
const regex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
||||
const match = regex.exec(input);
|
||||
|
||||
const prefix = match[1];
|
||||
const label = normalizeVarNames(match[2]); // Ensure the label name is normalized as it should be in the var stack.
|
||||
|
||||
//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(prefix[0] == '$' && 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(new RegExp(`(?<!\\w)(${variable})(?!\\w)`, 'g'), foundVar.content);
|
||||
});
|
||||
|
||||
try {
|
||||
return mathParser.evaluate(replacedLabel);
|
||||
} catch (error) {
|
||||
return undefined; // Return undefined if invalid math result
|
||||
}
|
||||
}
|
||||
//^=====--------------------< HANDLE MATH >-------------------=====^//
|
||||
|
||||
const foundVar = lookupVar(label, globalPageNumber, hoist);
|
||||
|
||||
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] ? normalizeVarNames(match[4]) : null;
|
||||
const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Normalize text content (except newlines for block-level content)
|
||||
|
||||
varsQueue.push(
|
||||
{ type : 'varDefBlock',
|
||||
varName : label,
|
||||
content : content
|
||||
});
|
||||
}
|
||||
if(match[6]) { // Block Call
|
||||
const label = match[7] ? normalizeVarNames(match[7]) : null;
|
||||
|
||||
varsQueue.push(
|
||||
{ type : 'varCallBlock',
|
||||
varName : label,
|
||||
content : match[0]
|
||||
});
|
||||
}
|
||||
if(match[8]) { // Inline Definition
|
||||
const label = match[10] ? normalizeVarNames(match[10]) : null;
|
||||
let content = match[11] || null;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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] ? normalizeVarNames(match[13]) : null;
|
||||
|
||||
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 >-------------------=====^//
|
||||
|
||||
// Emoji options
|
||||
// To add more icon fonts, need to do these things
|
||||
// 1) Add the font file as .woff2 to themes/fonts/iconFonts folder
|
||||
@@ -807,25 +483,17 @@ const mergeHTMLTags = (originalTags, newTags)=>{
|
||||
};
|
||||
};
|
||||
|
||||
const globalVarsList = {};
|
||||
let varsQueue = [];
|
||||
let globalPageNumber = 0;
|
||||
|
||||
const Markdown = {
|
||||
marked : Marked,
|
||||
render : (rawBrewText, pageNumber=0)=>{
|
||||
const lastPageNumber = pageNumber > 0 ? globalVarsList[pageNumber - 1].HB_pageNumber.content : 0;
|
||||
globalVarsList[pageNumber] = { //Reset global links for current page, to ensure values are parsed in order
|
||||
'HB_pageNumber' : { //Add document variables for this page
|
||||
content : !isNaN(Number(lastPageNumber)) ? Number(lastPageNumber) + 1 : lastPageNumber,
|
||||
resolved : true
|
||||
}
|
||||
};
|
||||
varsQueue = []; //Could move into MarkedVariables()
|
||||
globalPageNumber = pageNumber;
|
||||
if(pageNumber==0) {
|
||||
MarkedGFMResetHeadingIDs();
|
||||
}
|
||||
setMarkedVariablePage(pageNumber);
|
||||
|
||||
const lastPageNumber = pageNumber > 0 ? getMarkedVariable('HB_pageNumber', pageNumber - 1) : 0;
|
||||
setMarkedVariable('HB_pageNumber', //Add document variables for this page
|
||||
!isNaN(Number(lastPageNumber)) ? Number(lastPageNumber) + 1 : lastPageNumber,
|
||||
pageNumber);
|
||||
|
||||
if(pageNumber==0) MarkedGFMResetHeadingIDs();
|
||||
|
||||
rawBrewText = rawBrewText.replace(/^\\column(?:break)?$/gm, `\n<div class='columnSplit'></div>\n`);
|
||||
|
||||
@@ -49,7 +49,7 @@ const cleanUrl = function (sanitize, base, href) {
|
||||
prot = decodeURIComponent(unescape(href))
|
||||
.replace(nonWordAndColonTest, '')
|
||||
.toLowerCase();
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if(prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
|
||||
@@ -58,7 +58,7 @@ const cleanUrl = function (sanitize, base, href) {
|
||||
}
|
||||
try {
|
||||
href = encodeURI(href).replace(/%25/g, '%');
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
@@ -1,84 +0,0 @@
|
||||
import diceFont from '../../../themes/fonts/iconFonts/diceFont.js';
|
||||
import elderberryInn from '../../../themes/fonts/iconFonts/elderberryInn.js';
|
||||
import fontAwesome from '../../../themes/fonts/iconFonts/fontAwesome.js';
|
||||
import gameIcons from '../../../themes/fonts/iconFonts/gameIcons.js';
|
||||
|
||||
const emojis = {
|
||||
...diceFont,
|
||||
...elderberryInn,
|
||||
...fontAwesome,
|
||||
...gameIcons
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
const autoCloseCurlyBraces = function(CodeMirror, cm, typingClosingBrace) {
|
||||
const ranges = cm.listSelections(), replacements = [];
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
if(!ranges[i].empty()) return CodeMirror.Pass;
|
||||
const pos = ranges[i].head, line = cm.getLine(pos.line), tok = cm.getTokenAt(pos);
|
||||
if(!typingClosingBrace && (tok.type == 'string' || tok.string.charAt(0) != '{' || tok.start != pos.ch - 1))
|
||||
return CodeMirror.Pass;
|
||||
else if(typingClosingBrace) {
|
||||
let hasUnclosedBraces = false, index = -1;
|
||||
do {
|
||||
index = line.indexOf('{{', index + 1);
|
||||
if(index !== -1 && line.indexOf('}}', index + 1) === -1) {
|
||||
hasUnclosedBraces = true;
|
||||
break;
|
||||
}
|
||||
} while (index !== -1);
|
||||
if(!hasUnclosedBraces) return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
replacements[i] = typingClosingBrace ? {
|
||||
text : '}}',
|
||||
newPos : CodeMirror.Pos(pos.line, pos.ch + 2)
|
||||
} : {
|
||||
text : '{}}',
|
||||
newPos : CodeMirror.Pos(pos.line, pos.ch + 1)
|
||||
};
|
||||
}
|
||||
|
||||
for (let i = ranges.length - 1; i >= 0; i--) {
|
||||
const info = replacements[i];
|
||||
cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, '+insert');
|
||||
const sel = cm.listSelections().slice(0);
|
||||
sel[i] = {
|
||||
head : info.newPos,
|
||||
anchor : info.newPos
|
||||
};
|
||||
cm.setSelections(sel);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
autoCloseCurlyBraces : function(CodeMirror, codeMirror) {
|
||||
const map = { name: 'autoCloseCurlyBraces' };
|
||||
map[`'{'`] = function(cm) { return autoCloseCurlyBraces(CodeMirror, cm); };
|
||||
map[`'}'`] = function(cm) { return autoCloseCurlyBraces(CodeMirror, cm, true); };
|
||||
codeMirror.addKeyMap(map);
|
||||
}
|
||||
};
|
||||
@@ -1,465 +0,0 @@
|
||||
/* eslint-disable max-lines */
|
||||
require('./codeEditor.less');
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
const closeTag = require('./close-tag');
|
||||
const autoCompleteEmoji = require('./autocompleteEmoji');
|
||||
|
||||
let CodeMirror;
|
||||
if(typeof window !== 'undefined'){
|
||||
CodeMirror = require('codemirror');
|
||||
|
||||
//Language Modes
|
||||
require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown
|
||||
require('codemirror/mode/css/css.js');
|
||||
require('codemirror/mode/javascript/javascript.js');
|
||||
|
||||
//Addons
|
||||
//Code folding
|
||||
require('codemirror/addon/fold/foldcode.js');
|
||||
require('codemirror/addon/fold/foldgutter.js');
|
||||
//Search and replace
|
||||
require('codemirror/addon/search/search.js');
|
||||
require('codemirror/addon/search/searchcursor.js');
|
||||
require('codemirror/addon/search/jump-to-line.js');
|
||||
require('codemirror/addon/search/match-highlighter.js');
|
||||
require('codemirror/addon/search/matchesonscrollbar.js');
|
||||
require('codemirror/addon/dialog/dialog.js');
|
||||
//Trailing space highlighting
|
||||
// require('codemirror/addon/edit/trailingspace.js');
|
||||
//Active line highlighting
|
||||
// require('codemirror/addon/selection/active-line.js');
|
||||
//Scroll past last line
|
||||
require('codemirror/addon/scroll/scrollpastend.js');
|
||||
//Auto-closing
|
||||
//XML code folding is a requirement of the auto-closing tag feature and is not enabled
|
||||
require('codemirror/addon/fold/xml-fold.js');
|
||||
require('codemirror/addon/edit/closetag.js');
|
||||
//Autocompletion
|
||||
require('codemirror/addon/hint/show-hint.js');
|
||||
|
||||
const foldPagesCode = require('./fold-pages');
|
||||
foldPagesCode.registerHomebreweryHelper(CodeMirror);
|
||||
const foldCSSCode = require('./fold-css');
|
||||
foldCSSCode.registerHomebreweryHelper(CodeMirror);
|
||||
}
|
||||
|
||||
const CodeEditor = createClass({
|
||||
displayName : 'CodeEditor',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
language : '',
|
||||
value : '',
|
||||
wrap : true,
|
||||
onChange : ()=>{},
|
||||
enableFolding : true,
|
||||
editorTheme : 'default'
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState : function() {
|
||||
return {
|
||||
docs : {}
|
||||
};
|
||||
},
|
||||
|
||||
editor : React.createRef(null),
|
||||
|
||||
componentDidMount : function() {
|
||||
this.buildEditor();
|
||||
const newDoc = CodeMirror.Doc(this.props.value, this.props.language);
|
||||
this.codeMirror.swapDoc(newDoc);
|
||||
},
|
||||
|
||||
componentDidUpdate : function(prevProps) {
|
||||
if(prevProps.view !== this.props.view){ //view changed; swap documents
|
||||
let newDoc;
|
||||
|
||||
if(!this.state.docs[this.props.view]) {
|
||||
newDoc = CodeMirror.Doc(this.props.value, this.props.language);
|
||||
} else {
|
||||
newDoc = this.state.docs[this.props.view];
|
||||
}
|
||||
|
||||
const oldDoc = { [prevProps.view]: this.codeMirror.swapDoc(newDoc) };
|
||||
|
||||
this.setState((prevState)=>({
|
||||
docs : _.merge({}, prevState.docs, oldDoc)
|
||||
}));
|
||||
|
||||
this.props.rerenderParent();
|
||||
} else if(this.codeMirror?.getValue() != this.props.value) { //update editor contents if brew.text is changed from outside
|
||||
this.codeMirror.setValue(this.props.value);
|
||||
}
|
||||
|
||||
if(this.props.enableFolding) {
|
||||
this.codeMirror.setOption('foldOptions', this.foldOptions(this.codeMirror));
|
||||
} else {
|
||||
this.codeMirror.setOption('foldOptions', false);
|
||||
}
|
||||
|
||||
if(prevProps.editorTheme !== this.props.editorTheme){
|
||||
this.codeMirror.setOption('theme', this.props.editorTheme);
|
||||
}
|
||||
},
|
||||
|
||||
buildEditor : function() {
|
||||
this.codeMirror = CodeMirror(this.editor.current, {
|
||||
lineNumbers : true,
|
||||
lineWrapping : this.props.wrap,
|
||||
indentWithTabs : false,
|
||||
tabSize : 2,
|
||||
smartIndent : false,
|
||||
historyEventDelay : 250,
|
||||
scrollPastEnd : true,
|
||||
extraKeys : {
|
||||
'Tab' : this.indent,
|
||||
'Shift-Tab' : this.dedent,
|
||||
'Ctrl-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,
|
||||
'Cmd-I' : this.makeItalic,
|
||||
'Ctrl-U' : this.makeUnderline,
|
||||
'Cmd-U' : this.makeUnderline,
|
||||
'Ctrl-.' : this.makeNbsp,
|
||||
'Cmd-.' : this.makeNbsp,
|
||||
'Shift-Ctrl-.' : this.makeSpace,
|
||||
'Shift-Cmd-.' : this.makeSpace,
|
||||
'Shift-Ctrl-,' : this.removeSpace,
|
||||
'Shift-Cmd-,' : this.removeSpace,
|
||||
'Ctrl-M' : this.makeSpan,
|
||||
'Cmd-M' : this.makeSpan,
|
||||
'Shift-Ctrl-M' : this.makeDiv,
|
||||
'Shift-Cmd-M' : this.makeDiv,
|
||||
'Ctrl-/' : this.makeComment,
|
||||
'Cmd-/' : this.makeComment,
|
||||
'Ctrl-K' : this.makeLink,
|
||||
'Cmd-K' : this.makeLink,
|
||||
'Ctrl-L' : ()=>this.makeList('UL'),
|
||||
'Cmd-L' : ()=>this.makeList('UL'),
|
||||
'Shift-Ctrl-L' : ()=>this.makeList('OL'),
|
||||
'Shift-Cmd-L' : ()=>this.makeList('OL'),
|
||||
'Shift-Ctrl-1' : ()=>this.makeHeader(1),
|
||||
'Shift-Ctrl-2' : ()=>this.makeHeader(2),
|
||||
'Shift-Ctrl-3' : ()=>this.makeHeader(3),
|
||||
'Shift-Ctrl-4' : ()=>this.makeHeader(4),
|
||||
'Shift-Ctrl-5' : ()=>this.makeHeader(5),
|
||||
'Shift-Ctrl-6' : ()=>this.makeHeader(6),
|
||||
'Shift-Cmd-1' : ()=>this.makeHeader(1),
|
||||
'Shift-Cmd-2' : ()=>this.makeHeader(2),
|
||||
'Shift-Cmd-3' : ()=>this.makeHeader(3),
|
||||
'Shift-Cmd-4' : ()=>this.makeHeader(4),
|
||||
'Shift-Cmd-5' : ()=>this.makeHeader(5),
|
||||
'Shift-Cmd-6' : ()=>this.makeHeader(6),
|
||||
'Shift-Ctrl-Enter' : this.newColumn,
|
||||
'Shift-Cmd-Enter' : this.newColumn,
|
||||
'Ctrl-Enter' : this.newPage,
|
||||
'Cmd-Enter' : this.newPage,
|
||||
'Ctrl-F' : 'findPersistent',
|
||||
'Cmd-F' : 'findPersistent',
|
||||
'Shift-Enter' : 'findPersistentPrevious',
|
||||
'Ctrl-[' : this.foldAllCode,
|
||||
'Cmd-[' : this.foldAllCode,
|
||||
'Ctrl-]' : this.unfoldAllCode,
|
||||
'Cmd-]' : this.unfoldAllCode
|
||||
},
|
||||
foldGutter : true,
|
||||
foldOptions : this.foldOptions(this.codeMirror),
|
||||
gutters : ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
autoCloseTags : true,
|
||||
styleActiveLine : true,
|
||||
showTrailingSpace : false,
|
||||
theme : this.props.editorTheme
|
||||
// specialChars : / /,
|
||||
// specialCharPlaceholder : function(char) {
|
||||
// const el = document.createElement('span');
|
||||
// el.className = 'cm-space';
|
||||
// el.innerHTML = ' ';
|
||||
// return el;
|
||||
// }
|
||||
});
|
||||
|
||||
// Add custom behaviors (auto-close curlies and auto-complete emojis)
|
||||
closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror);
|
||||
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
|
||||
|
||||
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
|
||||
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
|
||||
this.updateSize();
|
||||
},
|
||||
|
||||
indent : function () {
|
||||
const cm = this.codeMirror;
|
||||
if(cm.somethingSelected()) {
|
||||
cm.execCommand('indentMore');
|
||||
} else {
|
||||
cm.execCommand('insertSoftTab');
|
||||
}
|
||||
},
|
||||
|
||||
dedent : function () {
|
||||
this.codeMirror.execCommand('indentLess');
|
||||
},
|
||||
|
||||
makeHeader : function (number) {
|
||||
const selection = this.codeMirror.getSelection();
|
||||
const header = Array(number).fill('#').join('');
|
||||
this.codeMirror.replaceSelection(`${header} ${selection}`, 'around');
|
||||
const cursor = this.codeMirror.getCursor();
|
||||
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch + selection.length + number + 1 });
|
||||
},
|
||||
|
||||
makeBold : 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 });
|
||||
}
|
||||
},
|
||||
|
||||
makeItalic : 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 });
|
||||
}
|
||||
},
|
||||
|
||||
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() {
|
||||
this.codeMirror.replaceSelection(' ', 'end');
|
||||
},
|
||||
|
||||
makeSpace : function() {
|
||||
const selection = this.codeMirror.getSelection();
|
||||
const t = selection.slice(0, 8) === '{{width:' && selection.slice(0 -4) === '% }}';
|
||||
if(t){
|
||||
const percent = parseInt(selection.slice(8, -4)) + 10;
|
||||
this.codeMirror.replaceSelection(percent < 90 ? `{{width:${percent}% }}` : '{{width:100% }}', 'around');
|
||||
} else {
|
||||
this.codeMirror.replaceSelection(`{{width:10% }}`, 'around');
|
||||
}
|
||||
},
|
||||
|
||||
removeSpace : function() {
|
||||
const selection = this.codeMirror.getSelection();
|
||||
const t = selection.slice(0, 8) === '{{width:' && selection.slice(0 -4) === '% }}';
|
||||
if(t){
|
||||
const percent = parseInt(selection.slice(8, -4)) - 10;
|
||||
this.codeMirror.replaceSelection(percent > 10 ? `{{width:${percent}% }}` : '', 'around');
|
||||
}
|
||||
},
|
||||
|
||||
newColumn : function() {
|
||||
this.codeMirror.replaceSelection('\n\\column\n\n', 'end');
|
||||
},
|
||||
|
||||
newPage : function() {
|
||||
this.codeMirror.replaceSelection('\n\\page\n\n', 'end');
|
||||
},
|
||||
|
||||
injectText : function(injectText, overwrite=true) {
|
||||
const cm = this.codeMirror;
|
||||
if(!overwrite) {
|
||||
cm.setCursor(cm.getCursor('from'));
|
||||
}
|
||||
cm.replaceSelection(injectText, 'end');
|
||||
cm.focus();
|
||||
},
|
||||
|
||||
makeUnderline : function() {
|
||||
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 3) === '<u>' && selection.slice(-4) === '</u>';
|
||||
this.codeMirror.replaceSelection(t ? selection.slice(3, -4) : `<u>${selection}</u>`, 'around');
|
||||
if(selection.length === 0){
|
||||
const cursor = this.codeMirror.getCursor();
|
||||
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 4 });
|
||||
}
|
||||
},
|
||||
|
||||
makeSpan : 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 });
|
||||
}
|
||||
},
|
||||
|
||||
makeDiv : function() {
|
||||
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '{{' && selection.slice(-2) === '}}';
|
||||
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `{{\n${selection}\n}}`, 'around');
|
||||
if(selection.length === 0){
|
||||
const cursor = this.codeMirror.getCursor();
|
||||
this.codeMirror.setCursor({ line: cursor.line - 1, ch: cursor.ch }); // set to -2? if wanting to enter classes etc. if so, get rid of first \n when replacing selection
|
||||
}
|
||||
},
|
||||
|
||||
makeComment : function() {
|
||||
let regex;
|
||||
let cursorPos;
|
||||
let newComment;
|
||||
const selection = this.codeMirror.getSelection();
|
||||
if(this.props.language === 'gfm'){
|
||||
regex = /^\s*(<!--\s?)(.*?)(\s?-->)\s*$/gs;
|
||||
cursorPos = 4;
|
||||
newComment = `<!-- ${selection} -->`;
|
||||
} else {
|
||||
regex = /^\s*(\/\*\s?)(.*?)(\s?\*\/)\s*$/gs;
|
||||
cursorPos = 3;
|
||||
newComment = `/* ${selection} */`;
|
||||
}
|
||||
this.codeMirror.replaceSelection(regex.test(selection) == true ? selection.replace(regex, '$2') : newComment, 'around');
|
||||
if(selection.length === 0){
|
||||
const cursor = this.codeMirror.getCursor();
|
||||
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - cursorPos });
|
||||
};
|
||||
},
|
||||
|
||||
makeLink : function() {
|
||||
const isLink = /^\[(.*)\]\((.*)\)$/;
|
||||
const selection = this.codeMirror.getSelection().trim();
|
||||
let match;
|
||||
if(match = isLink.exec(selection)){
|
||||
const altText = match[1];
|
||||
const url = match[2];
|
||||
this.codeMirror.replaceSelection(`${altText} ${url}`);
|
||||
const cursor = this.codeMirror.getCursor();
|
||||
this.codeMirror.setSelection({ line: cursor.line, ch: cursor.ch - url.length }, { line: cursor.line, ch: cursor.ch });
|
||||
} else {
|
||||
this.codeMirror.replaceSelection(`[${selection || 'alt text'}](url)`);
|
||||
const cursor = this.codeMirror.getCursor();
|
||||
this.codeMirror.setSelection({ line: cursor.line, ch: cursor.ch - 4 }, { line: cursor.line, ch: cursor.ch - 1 });
|
||||
}
|
||||
},
|
||||
|
||||
makeList : function(listType) {
|
||||
const selectionStart = this.codeMirror.getCursor('from'), selectionEnd = this.codeMirror.getCursor('to');
|
||||
this.codeMirror.setSelection(
|
||||
{ line: selectionStart.line, ch: 0 },
|
||||
{ line: selectionEnd.line, ch: this.codeMirror.getLine(selectionEnd.line).length }
|
||||
);
|
||||
const newSelection = this.codeMirror.getSelection();
|
||||
|
||||
const regex = /^\d+\.\s|^-\s/gm;
|
||||
if(newSelection.match(regex) != null){ // if selection IS A LIST
|
||||
this.codeMirror.replaceSelection(newSelection.replace(regex, ''), 'around');
|
||||
} else { // if selection IS NOT A LIST
|
||||
listType == 'UL' ? this.codeMirror.replaceSelection(newSelection.replace(/^/gm, `- `), 'around') :
|
||||
this.codeMirror.replaceSelection(newSelection.replace(/^/gm, (()=>{
|
||||
let n = 1;
|
||||
return ()=>{
|
||||
return `${n++}. `;
|
||||
};
|
||||
})()), 'around');
|
||||
}
|
||||
},
|
||||
|
||||
foldAllCode : function() {
|
||||
this.codeMirror.execCommand('foldAll');
|
||||
},
|
||||
|
||||
unfoldAllCode : function() {
|
||||
this.codeMirror.execCommand('unfoldAll');
|
||||
},
|
||||
|
||||
//=-- Externally used -==//
|
||||
setCursorPosition : function(line, char){
|
||||
setTimeout(()=>{
|
||||
this.codeMirror.focus();
|
||||
this.codeMirror.doc.setCursor(line, char);
|
||||
}, 10);
|
||||
},
|
||||
getCursorPosition : function(){
|
||||
return this.codeMirror.getCursor();
|
||||
},
|
||||
getTopVisibleLine : function(){
|
||||
const rect = this.codeMirror.getWrapperElement().getBoundingClientRect();
|
||||
const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, 'window');
|
||||
return topVisibleLine;
|
||||
},
|
||||
updateSize : function(){
|
||||
this.codeMirror.refresh();
|
||||
},
|
||||
redo : function(){
|
||||
return this.codeMirror.redo();
|
||||
},
|
||||
undo : function(){
|
||||
return this.codeMirror.undo();
|
||||
},
|
||||
historySize : function(){
|
||||
return this.codeMirror.doc.historySize();
|
||||
},
|
||||
|
||||
foldOptions : function(cm){
|
||||
return {
|
||||
scanUp : true,
|
||||
rangeFinder : this.props.language === 'css' ? CodeMirror.fold.homebrewerycss : CodeMirror.fold.homebrewery,
|
||||
widget : (from, to)=>{
|
||||
let text = '';
|
||||
let currentLine = from.line;
|
||||
let maxLength = 50;
|
||||
|
||||
let foldPreviewText = '';
|
||||
while (currentLine <= to.line && text.length <= maxLength) {
|
||||
const currentText = this.codeMirror.getLine(currentLine);
|
||||
currentLine++;
|
||||
if(currentText[0] == '#'){
|
||||
foldPreviewText = currentText;
|
||||
break;
|
||||
}
|
||||
if(!foldPreviewText && currentText != '\n') {
|
||||
foldPreviewText = currentText;
|
||||
}
|
||||
}
|
||||
text = foldPreviewText || `Lines ${from.line+1}-${to.line+1}`;
|
||||
text = text.replace('{', '').trim();
|
||||
|
||||
// Truncate data URLs at `data:`
|
||||
const startOfData = text.indexOf('data:');
|
||||
if(startOfData > 0)
|
||||
maxLength = Math.min(startOfData + 5, maxLength);
|
||||
|
||||
if(text.length > maxLength)
|
||||
text = `${text.slice(0, maxLength)}...`;
|
||||
|
||||
return `\u21A4 ${text} \u21A6`;
|
||||
}
|
||||
};
|
||||
},
|
||||
//----------------------//
|
||||
|
||||
render : function(){
|
||||
return <>
|
||||
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} type='text/css' rel='stylesheet' />
|
||||
<div className='codeEditor' ref={this.editor} style={this.props.style}/>
|
||||
</>;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = CodeEditor;
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
@import (less) 'codemirror/lib/codemirror.css';
|
||||
@import (less) 'codemirror/addon/fold/foldgutter.css';
|
||||
@import (less) 'codemirror/addon/search/matchesonscrollbar.css';
|
||||
@import (less) 'codemirror/addon/dialog/dialog.css';
|
||||
@import (less) 'codemirror/addon/hint/show-hint.css';
|
||||
|
||||
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
||||
@import (less) './themes/fonts/iconFonts/diceFont.less';
|
||||
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
|
||||
@import (less) './themes/fonts/iconFonts/gameIcons.less';
|
||||
@import (less) './themes/fonts/iconFonts/fontAwesome.less';
|
||||
|
||||
@keyframes sourceMoveAnimation {
|
||||
50% { color : white;background-color : red;}
|
||||
100% { color : unset;background-color : unset;}
|
||||
}
|
||||
|
||||
.codeEditor {
|
||||
@media screen and (pointer : coarse) {
|
||||
font-size : 16px;
|
||||
}
|
||||
.CodeMirror-foldmarker {
|
||||
font-family : inherit;
|
||||
font-weight : 600;
|
||||
color : grey;
|
||||
text-shadow : none;
|
||||
}
|
||||
|
||||
.CodeMirror-foldgutter {
|
||||
cursor : pointer;
|
||||
border-left : 1px solid #EEEEEE;
|
||||
transition : background 0.1s;
|
||||
&:hover { background : #DDDDDD; }
|
||||
}
|
||||
|
||||
.sourceMoveFlash .CodeMirror-line {
|
||||
animation-name : sourceMoveAnimation;
|
||||
animation-duration : 0.4s;
|
||||
}
|
||||
|
||||
.CodeMirror-vscrollbar {
|
||||
&::-webkit-scrollbar { width : 20px; }
|
||||
&::-webkit-scrollbar-thumb {
|
||||
width : 20px;
|
||||
background : linear-gradient(90deg, #858585 15px, #808080 15px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//.cm-tab {
|
||||
// background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAQAAACOs/baAAAARUlEQVR4nGJgIAG8JkXxUAcCtDWemcGR1lY4MvgzCEKY7jSBjgxBDAG09UEQzAe0AMwMHrSOAwEGRtpaMIwAAAAA//8DAG4ID9EKs6YqAAAAAElFTkSuQmCC) no-repeat right;
|
||||
//}
|
||||
|
||||
//.cm-trailingspace {
|
||||
// .cm-space {
|
||||
// background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQAgMAAABW5NbuAAAACVBMVEVHcEwAAAAAAAAWawmTAAAAA3RSTlMAPBJ6PMxpAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAFUlEQVQI12NgwACcCQysASAEZGAAACMuAX06aCQUAAAAAElFTkSuQmCC) no-repeat right;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
.emojiPreview {
|
||||
font-size : 1.5em;
|
||||
line-height : 1.2em;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
module.exports = {
|
||||
registerHomebreweryHelper : function(CodeMirror) {
|
||||
CodeMirror.registerHelper('fold', 'homebrewerycss', function(cm, start) {
|
||||
|
||||
// BRACE FOLDING
|
||||
const startMatcher = /\{[ \t]*$/;
|
||||
const endMatcher = /\}[ \t]*$/;
|
||||
const activeLine = cm.getLine(start.line);
|
||||
|
||||
|
||||
if(activeLine.match(startMatcher)) {
|
||||
const lastLineNo = cm.lastLine();
|
||||
let end = start.line + 1;
|
||||
let braceCount = 1;
|
||||
|
||||
while (end < lastLineNo) {
|
||||
const curLine = cm.getLine(end);
|
||||
if(curLine.match(startMatcher)) braceCount++;
|
||||
if(curLine.match(endMatcher)) braceCount--;
|
||||
if(braceCount == 0) break;
|
||||
++end;
|
||||
}
|
||||
|
||||
return {
|
||||
from : CodeMirror.Pos(start.line, 0),
|
||||
to : CodeMirror.Pos(end, cm.getLine(end).length)
|
||||
};
|
||||
}
|
||||
|
||||
// @import and data-url folding
|
||||
const importMatcher = /^@import.*?;/;
|
||||
const dataURLMatcher = /url\(.*?data\:.*\)/;
|
||||
|
||||
if(activeLine.match(importMatcher) || activeLine.match(dataURLMatcher)) {
|
||||
return {
|
||||
from : CodeMirror.Pos(start.line, 0),
|
||||
to : CodeMirror.Pos(start.line, activeLine.length)
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
module.exports = {
|
||||
registerHomebreweryHelper : function(CodeMirror) {
|
||||
CodeMirror.registerHelper('fold', 'homebrewery', function(cm, start) {
|
||||
const matcher = /^\\page.*/;
|
||||
const prevLine = cm.getLine(start.line - 1);
|
||||
|
||||
if(start.line === cm.firstLine() || prevLine.match(matcher)) {
|
||||
const lastLineNo = cm.lastLine();
|
||||
let end = start.line;
|
||||
|
||||
while (end < lastLineNo) {
|
||||
if(cm.getLine(end + 1).match(matcher))
|
||||
break;
|
||||
++end;
|
||||
}
|
||||
|
||||
return {
|
||||
from : CodeMirror.Pos(start.line, 0),
|
||||
to : CodeMirror.Pos(end, cm.getLine(end).length)
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,120 +0,0 @@
|
||||
require('client/homebrew/navbar/navbar.less');
|
||||
const React = require('react');
|
||||
const { useState, useRef, useEffect } = React;
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
||||
|
||||
const Nav = {
|
||||
base : createClass({
|
||||
displayName : 'Nav.base',
|
||||
render : function(){
|
||||
return <nav>
|
||||
{this.props.children}
|
||||
</nav>;
|
||||
}
|
||||
}),
|
||||
logo : function(){
|
||||
return <a className='navLogo' href='https://www.naturalcrit.com'>
|
||||
<NaturalCritIcon />
|
||||
<span className='name'>
|
||||
Natural<span className='crit'>Crit</span>
|
||||
</span>
|
||||
</a>;
|
||||
},
|
||||
|
||||
section : createClass({
|
||||
displayName : 'Nav.section',
|
||||
render : function(){
|
||||
return <div className={`navSection ${this.props.className ?? ''}`}>
|
||||
{this.props.children}
|
||||
</div>;
|
||||
}
|
||||
}),
|
||||
|
||||
item : createClass({
|
||||
displayName : 'Nav.item',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
icon : null,
|
||||
href : null,
|
||||
newTab : false,
|
||||
onClick : function(){},
|
||||
color : null
|
||||
};
|
||||
},
|
||||
handleClick : function(e){
|
||||
this.props.onClick(e);
|
||||
},
|
||||
render : function(){
|
||||
const classes = cx('navItem', this.props.color, this.props.className);
|
||||
|
||||
let icon;
|
||||
if(this.props.icon) icon = <i className={this.props.icon} />;
|
||||
|
||||
const props = _.omit(this.props, ['newTab']);
|
||||
|
||||
if(this.props.href){
|
||||
return <a {...props} className={classes} target={this.props.newTab ? '_blank' : '_self'} >
|
||||
{this.props.children}
|
||||
{icon}
|
||||
</a>;
|
||||
} else {
|
||||
return <div {...props} className={classes} onClick={this.handleClick} >
|
||||
{this.props.children}
|
||||
{icon}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
dropdown : function dropdown(props) {
|
||||
props = Object.assign({}, props, {
|
||||
trigger : 'hover click'
|
||||
});
|
||||
|
||||
const myRef = useRef(null);
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
return ()=>{
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
function handleClickOutside(e) {
|
||||
// Close dropdown when clicked outside
|
||||
if(!myRef.current?.contains(e.target)) {
|
||||
handleDropdown(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDropdown(show) {
|
||||
setShowDropdown(show ?? !showDropdown);
|
||||
}
|
||||
|
||||
const dropdownChildren = React.Children.map(props.children, (child, i)=>{
|
||||
if(i < 1) return;
|
||||
return child;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`navDropdownContainer ${props.className ?? ''}`}
|
||||
ref={myRef}
|
||||
onMouseEnter = { props.trigger.includes('hover') ? ()=>handleDropdown(true) : undefined }
|
||||
onMouseLeave = { props.trigger.includes('hover') ? ()=>handleDropdown(false) : undefined }
|
||||
onClick = { props.trigger.includes('click') ? ()=>handleDropdown(true) : undefined }
|
||||
>
|
||||
{props.children[0] || props.children /*children is not an array when only one child*/}
|
||||
{showDropdown && <div className='navDropdown'>{dropdownChildren}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = Nav;
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function(props){
|
||||
return <svg version='1.1' x='0px' y='0px' viewBox='0 0 80 100' enableBackground='new 0 0 80 80'><g><g><polygon fill='#000000' points='12.9,71.4 7.6,66.1 19.3,54.4 20.7,55.8 10.4,66.1 12.9,68.6 23.2,58.3 24.6,59.7 '/></g><g><path fill='#000000' d='M29,61.6c-1.7,0-3.4-0.7-4.6-1.9l-5.1-5.1c-2.5-2.5-2.5-6.6,0-9.2l0.7-0.7L34.3,59l-0.7,0.7 C32.4,60.9,30.8,61.6,29,61.6z M20.1,47.6c-1.1,1.7-0.9,4.1,0.6,5.6l5.1,5.1c0.8,0.8,2,1.3,3.2,1.3c0.9,0,1.7-0.2,2.4-0.7 L20.1,47.6z'/></g><g><path fill='#000000' d='M12.3,74.8c-0.8,0-1.5-0.3-2-0.8l-5.2-5.2c-0.5-0.5-0.8-1.2-0.8-2c0-0.8,0.3-1.5,0.8-2 c1.1-1.1,2.9-1.1,4,0l5.2,5.2c1.1,1.1,1.1,2.9,0,4C13.8,74.5,13.1,74.8,12.3,74.8z M7.1,65.9c-0.2,0-0.4,0.1-0.6,0.2 c-0.2,0.2-0.2,0.4-0.2,0.6s0.1,0.4,0.2,0.6l5.2,5.2c0.3,0.3,0.9,0.3,1.2,0c0.3-0.3,0.3-0.8,0-1.2l-5.2-5.2 C7.5,66,7.3,65.9,7.1,65.9z'/></g><g><polygon fill='#000000' points='31.7,58.7 30.3,57.3 70,17.6 70,9 62.4,9 23.3,49.4 21.9,48 61.6,7 72,7 72,18.4 '/></g><g><rect x='46' y='6.7' transform='matrix(0.7168 0.6973 -0.6973 0.7168 35.9716 -23.568)' fill='#000000' width='2' height='51.6'/></g><g><rect x='13' y='61' fill='#000000' width='2' height='7'/></g><g><rect x='17' y='57' fill='#000000' width='2' height='7'/></g></g><g><g><polygon fill='#000000' points='68.4,71.4 56.7,59.7 58.1,58.3 68.4,68.6 70.8,66.1 60.5,55.8 61.9,54.4 73.6,66.1 '/></g><g><path fill='#000000' d='M52.2,61.6c-1.7,0-3.4-0.7-4.6-1.9L46.9,59l14.3-14.3l0.7,0.7c2.5,2.5,2.5,6.6,0,9.2l-5.1,5.1 C55.6,60.9,53.9,61.6,52.2,61.6z M49.8,58.9c0.7,0.4,1.5,0.7,2.4,0.7c1.2,0,2.3-0.5,3.2-1.3l5.1-5.1c1.5-1.5,1.7-3.8,0.6-5.6 L49.8,58.9z'/></g><g><path fill='#000000' d='M68.9,74.8c-0.8,0-1.5-0.3-2-0.8c-1.1-1.1-1.1-2.9,0-4l5.2-5.2c1.1-1.1,2.9-1.1,4,0c0.5,0.5,0.8,1.2,0.8,2 c0,0.8-0.3,1.5-0.8,2L70.9,74C70.4,74.5,69.7,74.8,68.9,74.8z M74.2,65.9c-0.2,0-0.4,0.1-0.6,0.2l-5.2,5.2c-0.3,0.3-0.3,0.8,0,1.2 c0.3,0.3,0.9,0.3,1.2,0l5.2-5.2c0.2-0.2,0.2-0.4,0.2-0.6s-0.1-0.4-0.2-0.6C74.6,66,74.4,65.9,74.2,65.9z'/></g><g><rect x='38.6' y='52.3' transform='matrix(0.7082 0.706 -0.706 0.7082 50.8397 -16.4875)' fill='#000000' width='13.4' height='2'/></g><g><polygon fill='#000000' points='30.6,39.9 9,18.4 9,7 19.7,7 41.1,29.1 39.7,30.5 18.8,9 11,9 11,17.6 32,38.5 '/></g><g><rect x='47.8' y='43.1' transform='matrix(0.6959 0.7181 -0.7181 0.6959 48.1381 -25.5246)' fill='#000000' width='12.8' height='2'/></g><g><rect x='12' y='23.1' transform='matrix(0.6974 0.7167 -0.7167 0.6974 25.1384 -11.3825)' fill='#000000' width='28.1' height='2'/></g><g><rect x='43.8' y='46.4' transform='matrix(0.6974 0.7167 -0.7167 0.6974 48.7492 -20.5985)' fill='#000000' width='10' height='2'/></g><g><rect x='66' y='61' fill='#000000' width='2' height='7'/></g><g><rect x='62' y='57' fill='#000000' width='2' height='7'/></g></g></svg>;
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
|
||||
module.exports = function(props){
|
||||
return <svg version='1.1' x='0px' y='0px' viewBox='0 0 90 112.5' enableBackground='new 0 0 90 90' >
|
||||
<path d='M25.363,25.54c0,1.906,8.793,3.454,19.636,3.454c10.848,0,19.638-1.547,19.638-3.454c0-1.12-3.056-2.117-7.774-2.75 c-1.418,1.891-3.659,3.133-6.208,3.133c-2.85,0-5.315-1.547-6.67-3.833C33.617,22.185,25.363,23.692,25.363,25.54z'/><path d='M84.075,54.142c0-8.68-2.868-17.005-8.144-23.829c1.106-1.399,1.41-2.771,1.41-3.854c0-6.574-10.245-9.358-19.264-10.533 c0.209,0.706,0.359,1.439,0.359,2.215c0,0.09-0.022,0.17-0.028,0.26l0,0c-0.028,0.853-0.195,1.667-0.479,2.429 c9.106,1.282,14.508,3.754,14.508,5.63c0,2.644-10.688,6.486-27.439,6.486c-16.748,0-27.438-3.842-27.438-6.486 c0-2.542,9.904-6.183,25.559-6.459c-0.098-0.396-0.159-0.807-0.2-1.223c0.006,0,0.013,0,0.017,0 c-0.017-0.213-0.063-0.417-0.063-0.636c0-1.084,0.226-2.119,0.628-3.058c-6.788,0.129-30.846,1.299-30.846,11.376 c0,1.083,0.305,2.455,1.411,3.854c-5.276,6.823-8.145,15.149-8.145,23.829c0,11.548,5.187,20.107,14.693,25.115 c-0.902,3.146-1.391,7.056,1.111,8.181c2.626,1.178,5.364-2.139,7.111-5.005c4.73,1.261,10.13,1.923,16.161,1.923 c6.034,0,11.428-0.661,16.158-1.922c1.75,2.865,4.493,6.18,7.112,5.004c2.504-1.123,2.014-5.035,1.113-8.179 C78.889,74.249,84.075,65.689,84.075,54.142z M70.39,31.392c5.43,6.046,8.78,14,8.78,22.75c0,20.919-18.582,25.309-34.171,25.309 c-15.587,0-34.17-4.39-34.17-25.309c0-8.75,3.35-16.7,8.781-22.753c5.561,2.643,15.502,4.009,25.389,4.009 C54.886,35.397,64.829,34.031,70.39,31.392z'/><path d='M50.654,23.374c2.892,0,5.234-2.341,5.234-5.233c0-2.887-2.343-5.23-5.234-5.23c-2.887,0-5.231,2.343-5.231,5.23 C45.423,21.032,47.768,23.374,50.654,23.374z'/>
|
||||
<circle cx='62.905' cy='10.089' r='3.595'/>
|
||||
<circle cx='52.616' cy='5.048' r='2.73'/>
|
||||
</svg>;
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
|
||||
module.exports = function(props){
|
||||
return <svg version='1.1' x='0px' y='0px' viewBox='0 0 100 100' enableBackground='new 0 0 100 100'><path d='M80.644,87.982l16.592-41.483c0.054-0.128,0.088-0.26,0.108-0.394c0.006-0.039,0.007-0.077,0.011-0.116 c0.007-0.087,0.008-0.174,0.002-0.26c-0.003-0.046-0.007-0.091-0.014-0.137c-0.014-0.089-0.036-0.176-0.063-0.262 c-0.012-0.034-0.019-0.069-0.031-0.103c-0.047-0.118-0.106-0.229-0.178-0.335c-0.004-0.006-0.006-0.012-0.01-0.018L67.999,3.358 c-0.01-0.013-0.003-0.026-0.013-0.04L68,3.315V4c0,0-0.033,0-0.037,0c-0.403-1-1.094-1.124-1.752-0.976 c0,0.004-0.004-0.012-0.007-0.012C66.201,3.016,66.194,3,66.194,3H66.19h-0.003h-0.003h-0.004h-0.003c0,0-0.004,0-0.007,0 s-0.003-0.151-0.007-0.151L20.495,15.227c-0.025,0.007-0.046-0.019-0.071-0.011c-0.087,0.028-0.172,0.041-0.253,0.083 c-0.054,0.027-0.102,0.053-0.152,0.085c-0.051,0.033-0.101,0.061-0.147,0.099c-0.044,0.036-0.084,0.073-0.124,0.113 c-0.048,0.048-0.093,0.098-0.136,0.152c-0.03,0.039-0.059,0.076-0.085,0.117c-0.046,0.07-0.084,0.145-0.12,0.223 c-0.011,0.023-0.027,0.042-0.036,0.066L2.911,57.664C2.891,57.715,3,57.768,3,57.82v0.002c0,0.186,0,0.375,0,0.562 c0,0.004,0,0.004,0,0.008c0,0,0,0,0,0.002c0,0,0,0,0,0.004v0.004v0.002c0,0.074-0.002,0.15,0.012,0.223 C3.015,58.631,3,58.631,3,58.633c0,0.004,0,0.004,0,0.008c0,0,0,0,0,0.002c0,0,0,0,0,0.004v0.004c0,0,0,0,0,0.002v0.004 c0,0.191-0.046,0.377,0.06,0.545c0-0.002-0.03,0.004-0.03,0.004c0,0.004-0.03,0.004-0.03,0.004c0,0.002,0,0.002,0,0.002 l-0.045,0.004c0.03,0.047,0.036,0.09,0.068,0.133l29.049,37.359c0.002,0.004,0,0.006,0.002,0.01c0.002,0.002,0,0.004,0.002,0.008 c0.006,0.008,0.014,0.014,0.021,0.021c0.024,0.029,0.052,0.051,0.078,0.078c0.027,0.029,0.053,0.057,0.082,0.082 c0.03,0.027,0.055,0.062,0.086,0.088c0.026,0.02,0.057,0.033,0.084,0.053c0.04,0.027,0.081,0.053,0.123,0.076 c0.005,0.004,0.01,0.008,0.016,0.01c0.087,0.051,0.176,0.09,0.269,0.123c0.042,0.014,0.082,0.031,0.125,0.043 c0.021,0.006,0.041,0.018,0.062,0.021c0.123,0.027,0.249,0.043,0.375,0.043c0.099,0,0.202-0.012,0.304-0.027l45.669-8.303 c0.057-0.01,0.108-0.021,0.163-0.037C79.547,88.992,79.562,89,79.575,89c0.004,0,0.004,0,0.004,0c0.021,0,0.039-0.027,0.06-0.035 c0.041-0.014,0.08-0.034,0.12-0.052c0.021-0.01,0.044-0.019,0.064-0.03c0.017-0.01,0.026-0.015,0.033-0.017 c0.014-0.008,0.023-0.021,0.037-0.028c0.14-0.078,0.269-0.174,0.38-0.285c0.014-0.016,0.024-0.034,0.038-0.048 c0.109-0.119,0.201-0.252,0.271-0.398c0.006-0.01,0.016-0.018,0.021-0.029c0.004-0.008,0.008-0.017,0.011-0.026 c0.002-0.004,0.003-0.006,0.005-0.01C80.627,88.021,80.635,88.002,80.644,87.982z M77.611,84.461L48.805,66.453l32.407-25.202 L77.611,84.461z M46.817,63.709L35.863,23.542l43.818,14.608L46.817,63.709z M84.668,40.542l8.926,5.952l-11.902,29.75 L84.668,40.542z M89.128,39.446L84.53,36.38l-6.129-12.257L89.128,39.446z M79.876,34.645L37.807,20.622L65.854,6.599L79.876,34.645 z M33.268,19.107l-6.485-2.162l23.781-6.487L33.268,19.107z M21.92,18.895l8.67,2.891L10.357,47.798L21.92,18.895z M32.652,24.649 l10.845,39.757L7.351,57.178L32.652,24.649z M43.472,67.857L32.969,92.363L8.462,60.855L43.472,67.857z M46.631,69.09l27.826,17.393 l-38.263,6.959L46.631,69.09z'></path></svg>;
|
||||
};
|
||||
Reference in New Issue
Block a user