diff --git a/package.json b/package.json index c8ef3e3df..1b5f28e0c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "description": "Create authentic looking D&D homebrews using only markdown", "version": "3.0.0", "scripts": { + "logs": "heroku logs -t --app=homebrewery", "dev": "node scripts/dev.js", "quick": "node scripts/quick.js", "build": "node scripts/build.js", @@ -29,7 +30,6 @@ "jwt-simple": "^0.5.1", "lodash": "^4.17.3", "loglevel": "^1.4.1", - "marked": "^0.3.5", "moment": "^2.11.0", "mongoose": "^4.3.3", "nconf": "^0.8.4", diff --git a/scripts/populate.js b/scripts/populate.js index da5418b00..1c4fe4d59 100644 --- a/scripts/populate.js +++ b/scripts/populate.js @@ -5,16 +5,20 @@ const DB = require('../server/db.js'); const BrewData = require('../server/brew.data.js'); //const BrewGen = require('../tests/brew.gen.js'); +const BrewGen = require('../shared/homebrewery/snippets/brew/brew.snippet.js'); + +const BREW_COUNT = 50; + return Promise.resolve() .then(DB.connect) .then(BrewData.removeAll) .then(() => { - console.log('Adding random brews...'); - return BrewGen.populateDB(BrewGen.random(50)); + return _.reduce(_.times(BREW_COUNT, BrewGen.brewModel), (flow, model)=>{ + return flow.then(()=>BrewData.create(model)) + }, Promise.resolve()); }) .then(() => { - console.log('Adding specific brews...'); - return BrewGen.populateDB(BrewGen.static()); + console.log(`Added ${BREW_COUNT} brews`); }) .then(() => { return DB.close(); diff --git a/shared/homebrewery/markdown.js b/shared/homebrewery/markdown.js index 288dedc08..652ad68f1 100644 --- a/shared/homebrewery/markdown.js +++ b/shared/homebrewery/markdown.js @@ -1,5 +1,6 @@ const _ = require('lodash'); -const Markdown = require('marked'); +//const Markdown = require('marked'); +const Markdown = require('./marked.lib.js'); const renderer = new Markdown.Renderer(); @@ -10,7 +11,9 @@ renderer.paragraph = function(text){ if(!matches) return `\n

${text}

\n`; let matchIndex = 0; const res = _.reduce(text.split(blockReg), (r, text) => { + //if(text) r.push(text); if(text) r.push(Markdown(text, {renderer : renderer, sanitize: true})); + const block = matches[matchIndex]; if(block && block[0] == '{'){ r.push(`\n\n
`); @@ -25,10 +28,14 @@ renderer.paragraph = function(text){ }, []).join('\n'); return res; }; - renderer.image = function(href, title, text){ return ``; }; +renderer.list = function(list, isOrdered, isDef){ + if(isDef) return ``; + if(isOrdered) return `
    ${list}
`; + return ``; +} module.exports = { @@ -36,9 +43,12 @@ module.exports = { render : (rawBrewText)=>{ blockCount = 0; - rawBrewText = rawBrewText.replace(/\\column/g, '{{columnSplit }}') + rawBrewText = rawBrewText.replace(/\\column/g, '{{columnSplit }}'); + + + let html = Markdown(rawBrewText,{renderer : renderer, sanitize: true}); + - let html = Markdown(rawBrewText, {renderer : renderer, sanitize: true}); //Close all hanging block tags html += _.times(blockCount, ()=>{return '
'}).join('\n'); return html; diff --git a/shared/homebrewery/marked.lib.js b/shared/homebrewery/marked.lib.js new file mode 100644 index 000000000..97c504146 --- /dev/null +++ b/shared/homebrewery/marked.lib.js @@ -0,0 +1,1288 @@ +/** + * marked - a markdown parser + * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) + * https://github.com/chjj/marked + */ + +;(function() { + +/** + * Block-Level Grammar + */ + +var block = { + newline: /^\n+/, + code: /^( {4}[^\n]+\n*)+/, + fences: noop, + hr: /^( *[-]){3,} *(?:\n+|$)/, + heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, + nptable: noop, + lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, + blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/, + list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/, + def: /^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, + table: noop, + paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, + text: /^[^\n]+/ +}; + +block.bullet = /(?:[*+-]|\d+\.)/; +block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; +block.item = replace(block.item, 'gm') + (/bull/g, block.bullet) + (); + +block.list = replace(block.list) + (/bull/g, block.bullet) + ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))') + ('def', '\\n+(?=' + block.def.source + ')') + (); + +block.blockquote = replace(block.blockquote) + ('def', block.def) + (); + +block._tag = '(?!(?:' + + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' + + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' + + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b'; + +block.html = replace(block.html) + ('comment', //) + ('closed', /<(tag)[\s\S]+?<\/\1>/) + ('closing', /])*?>/) + (/tag/g, block._tag) + (); + +block.paragraph = replace(block.paragraph) + ('hr', block.hr) + ('heading', block.heading) + ('lheading', block.lheading) + ('blockquote', block.blockquote) + ('tag', '<' + block._tag) + ('def', block.def) + (); + +/** + * Normal Block Grammar + */ + +block.normal = merge({}, block); + +/** + * GFM Block Grammar + */ + +block.gfm = merge({}, block.normal, { + fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/, + paragraph: /^/, + heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ +}); + +block.gfm.paragraph = replace(block.paragraph) + ('(?!', '(?!' + + block.gfm.fences.source.replace('\\1', '\\2') + '|' + + block.list.source.replace('\\1', '\\3') + '|') + (); + +/** + * GFM + Tables Block Grammar + */ + +block.tables = merge({}, block.gfm, { + nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, + table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/ +}); + +/** + * Block Lexer + */ + +function Lexer(options) { + this.tokens = []; + this.tokens.links = {}; + this.options = options || marked.defaults; + this.rules = block.normal; + + if (this.options.gfm) { + if (this.options.tables) { + this.rules = block.tables; + } else { + this.rules = block.gfm; + } + } +} + +/** + * Expose Block Rules + */ + +Lexer.rules = block; + +/** + * Static Lex Method + */ + +Lexer.lex = function(src, options) { + var lexer = new Lexer(options); + return lexer.lex(src); +}; + +/** + * Preprocessing + */ + +Lexer.prototype.lex = function(src) { + src = src + .replace(/\r\n|\r/g, '\n') + .replace(/\t/g, ' ') + .replace(/\u00a0/g, ' ') + .replace(/\u2424/g, '\n'); + + return this.token(src, true); +}; + +/** + * Lexing + */ + +Lexer.prototype.token = function(src, top, bq) { + var src = src.replace(/^ +$/gm, '') + , next + , loose + , cap + , bull + , b + , item + , space + , i + , l; + + while (src) { + // newline + if (cap = this.rules.newline.exec(src)) { + src = src.substring(cap[0].length); + if (cap[0].length > 1) { + this.tokens.push({ + type: 'space' + }); + } + } + + // code + if (cap = this.rules.code.exec(src)) { + src = src.substring(cap[0].length); + cap = cap[0].replace(/^ {4}/gm, ''); + this.tokens.push({ + type: 'code', + text: !this.options.pedantic + ? cap.replace(/\n+$/, '') + : cap + }); + continue; + } + + // fences (gfm) + if (cap = this.rules.fences.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'code', + lang: cap[2], + text: cap[3] || '' + }); + continue; + } + + // heading + if (cap = this.rules.heading.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'heading', + depth: cap[1].length, + text: cap[2] + }); + continue; + } + + // table no leading pipe (gfm) + if (top && (cap = this.rules.nptable.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: 'table', + header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3].replace(/\n$/, '').split('\n') + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i].split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + + // lheading + if (cap = this.rules.lheading.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'heading', + depth: cap[2] === '=' ? 1 : 2, + text: cap[1] + }); + continue; + } + + // hr + if (cap = this.rules.hr.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'hr' + }); + continue; + } + + // blockquote + if (cap = this.rules.blockquote.exec(src)) { + src = src.substring(cap[0].length); + + this.tokens.push({ + type: 'blockquote_start' + }); + + cap = cap[0].replace(/^ *> ?/gm, ''); + + // Pass `top` to keep the current + // "toplevel" state. This is exactly + // how markdown.pl works. + this.token(cap, top, true); + + this.tokens.push({ + type: 'blockquote_end' + }); + + continue; + } + + // list + if (cap = this.rules.list.exec(src)) { + src = src.substring(cap[0].length); + bull = cap[2]; + + this.tokens.push({ + type: 'list_start', + ordered: bull.length > 1, + defn: bull == '-' + }); + + // Get each top-level item. + cap = cap[0].match(this.rules.item); + + next = false; + l = cap.length; + i = 0; + + for (; i < l; i++) { + item = cap[i]; + + // Remove the list item's bullet + // so it is seen as the next token. + space = item.length; + item = item.replace(/^ *([*+-]|\d+\.) +/, ''); + + // Outdent whatever the + // list item contains. Hacky. + if (~item.indexOf('\n ')) { + space -= item.length; + item = !this.options.pedantic + ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') + : item.replace(/^ {1,4}/gm, ''); + } + + // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + if (this.options.smartLists && i !== l - 1) { + b = block.bullet.exec(cap[i + 1])[0]; + if (bull !== b && !(bull.length > 1 && b.length > 1)) { + src = cap.slice(i + 1).join('\n') + src; + i = l - 1; + } + } + + // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + loose = next || /\n\n(?!\s*$)/.test(item); + if (i !== l - 1) { + next = item.charAt(item.length - 1) === '\n'; + if (!loose) loose = next; + } + + this.tokens.push({ + type: loose + ? 'loose_item_start' + : 'list_item_start' + }); + + // Recurse. + this.token(item, false, bq); + + this.tokens.push({ + type: 'list_item_end' + }); + } + + this.tokens.push({ + type: 'list_end' + }); + + continue; + } + + // html + if (cap = this.rules.html.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: this.options.sanitize + ? 'paragraph' + : 'html', + pre: !this.options.sanitizer + && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), + text: cap[0] + }); + continue; + } + + // def + if ((!bq && top) && (cap = this.rules.def.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.links[cap[1].toLowerCase()] = { + href: cap[2], + title: cap[3] + }; + continue; + } + + // table (gfm) + if (top && (cap = this.rules.table.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: 'table', + header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i] + .replace(/^ *\| *| *\| *$/g, '') + .split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + + // top-level paragraph + if (top && (cap = this.rules.paragraph.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'paragraph', + text: cap[1].charAt(cap[1].length - 1) === '\n' + ? cap[1].slice(0, -1) + : cap[1] + }); + continue; + } + + // text + if (cap = this.rules.text.exec(src)) { + // Top-level should never reach here. + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'text', + text: cap[0] + }); + continue; + } + + if (src) { + throw new + Error('Infinite loop on byte: ' + src.charCodeAt(0)); + } + } + + return this.tokens; +}; + +/** + * Inline-Level Grammar + */ + +var inline = { + escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, + autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, + url: noop, + tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, + link: /^!?\[(inside)\]\(href\)/, + reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, + nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, + strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, + em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, + code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, + br: /^ {2,}\n(?!\s*$)/, + del: noop, + text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/; + +inline.link = replace(inline.link) + ('inside', inline._inside) + ('href', inline._href) + (); + +inline.reflink = replace(inline.reflink) + ('inside', inline._inside) + (); + +/** + * Normal Inline Grammar + */ + +inline.normal = merge({}, inline); + +/** + * Pedantic Inline Grammar + */ + +inline.pedantic = merge({}, inline.normal, { + strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/ +}); + +/** + * GFM Inline Grammar + */ + +inline.gfm = merge({}, inline.normal, { + escape: replace(inline.escape)('])', '~|])')(), + url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, + del: /^~~(?=\S)([\s\S]*?\S)~~/, + text: replace(inline.text) + (']|', '~]|') + ('|', '|https?://|') + () +}); + +/** + * GFM + Line Breaks Inline Grammar + */ + +inline.breaks = merge({}, inline.gfm, { + br: replace(inline.br)('{2,}', '*')(), + text: replace(inline.gfm.text)('{2,}', '*')() +}); + +/** + * Inline Lexer & Compiler + */ + +function InlineLexer(links, options) { + this.options = options || marked.defaults; + this.links = links; + this.rules = inline.normal; + this.renderer = this.options.renderer || new Renderer; + this.renderer.options = this.options; + + if (!this.links) { + throw new + Error('Tokens array requires a `links` property.'); + } + + if (this.options.gfm) { + if (this.options.breaks) { + this.rules = inline.breaks; + } else { + this.rules = inline.gfm; + } + } else if (this.options.pedantic) { + this.rules = inline.pedantic; + } +} + +/** + * Expose Inline Rules + */ + +InlineLexer.rules = inline; + +/** + * Static Lexing/Compiling Method + */ + +InlineLexer.output = function(src, links, options) { + var inline = new InlineLexer(links, options); + return inline.output(src); +}; + +/** + * Lexing/Compiling + */ + +InlineLexer.prototype.output = function(src) { + var out = '' + , link + , text + , href + , cap; + + while (src) { + // escape + if (cap = this.rules.escape.exec(src)) { + src = src.substring(cap[0].length); + out += cap[1]; + continue; + } + + // autolink + if (cap = this.rules.autolink.exec(src)) { + src = src.substring(cap[0].length); + if (cap[2] === '@') { + text = cap[1].charAt(6) === ':' + ? this.mangle(cap[1].substring(7)) + : this.mangle(cap[1]); + href = this.mangle('mailto:') + text; + } else { + text = escape(cap[1]); + href = text; + } + out += this.renderer.link(href, null, text); + continue; + } + + // url (gfm) + if (!this.inLink && (cap = this.rules.url.exec(src))) { + src = src.substring(cap[0].length); + text = escape(cap[1]); + href = text; + out += this.renderer.link(href, null, text); + continue; + } + + // tag + if (cap = this.rules.tag.exec(src)) { + if (!this.inLink && /^/i.test(cap[0])) { + this.inLink = false; + } + src = src.substring(cap[0].length); + out += this.options.sanitize + ? this.options.sanitizer + ? this.options.sanitizer(cap[0]) + : escape(cap[0]) + : cap[0] + continue; + } + + // link + if (cap = this.rules.link.exec(src)) { + src = src.substring(cap[0].length); + this.inLink = true; + out += this.outputLink(cap, { + href: cap[2], + title: cap[3] + }); + this.inLink = false; + continue; + } + + // reflink, nolink + if ((cap = this.rules.reflink.exec(src)) + || (cap = this.rules.nolink.exec(src))) { + src = src.substring(cap[0].length); + link = (cap[2] || cap[1]).replace(/\s+/g, ' '); + link = this.links[link.toLowerCase()]; + if (!link || !link.href) { + out += cap[0].charAt(0); + src = cap[0].substring(1) + src; + continue; + } + this.inLink = true; + out += this.outputLink(cap, link); + this.inLink = false; + continue; + } + + // strong + if (cap = this.rules.strong.exec(src)) { + src = src.substring(cap[0].length); + out += this.renderer.strong(this.output(cap[2] || cap[1])); + continue; + } + + // em + if (cap = this.rules.em.exec(src)) { + src = src.substring(cap[0].length); + out += this.renderer.em(this.output(cap[2] || cap[1])); + continue; + } + + // code + if (cap = this.rules.code.exec(src)) { + src = src.substring(cap[0].length); + out += this.renderer.codespan(escape(cap[2], true)); + continue; + } + + // br + if (cap = this.rules.br.exec(src)) { + src = src.substring(cap[0].length); + out += this.renderer.br(); + continue; + } + + // del (gfm) + if (cap = this.rules.del.exec(src)) { + src = src.substring(cap[0].length); + out += this.renderer.del(this.output(cap[1])); + continue; + } + + // text + if (cap = this.rules.text.exec(src)) { + src = src.substring(cap[0].length); + out += this.renderer.text(escape(this.smartypants(cap[0]))); + continue; + } + + if (src) { + throw new + Error('Infinite loop on byte: ' + src.charCodeAt(0)); + } + } + + return out; +}; + +/** + * Compile Link + */ + +InlineLexer.prototype.outputLink = function(cap, link) { + var href = escape(link.href) + , title = link.title ? escape(link.title) : null; + + return cap[0].charAt(0) !== '!' + ? this.renderer.link(href, title, this.output(cap[1])) + : this.renderer.image(href, title, escape(cap[1])); +}; + +/** + * Smartypants Transformations + */ + +InlineLexer.prototype.smartypants = function(text) { + if (!this.options.smartypants) return text; + return text + // em-dashes + .replace(/---/g, '\u2014') + // en-dashes + .replace(/--/g, '\u2013') + // opening singles + .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') + // closing singles & apostrophes + .replace(/'/g, '\u2019') + // opening doubles + .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') + // closing doubles + .replace(/"/g, '\u201d') + // ellipses + .replace(/\.{3}/g, '\u2026'); +}; + +/** + * Mangle Links + */ + +InlineLexer.prototype.mangle = function(text) { + if (!this.options.mangle) return text; + var out = '' + , l = text.length + , i = 0 + , ch; + + for (; i < l; i++) { + ch = text.charCodeAt(i); + if (Math.random() > 0.5) { + ch = 'x' + ch.toString(16); + } + out += '&#' + ch + ';'; + } + + return out; +}; + +/** + * Renderer + */ + +function Renderer(options) { + this.options = options || {}; +} + +Renderer.prototype.code = function(code, lang, escaped) { + if (this.options.highlight) { + var out = this.options.highlight(code, lang); + if (out != null && out !== code) { + escaped = true; + code = out; + } + } + + if (!lang) { + return '
'
+      + (escaped ? code : escape(code, true))
+      + '\n
'; + } + + return '
'
+    + (escaped ? code : escape(code, true))
+    + '\n
\n'; +}; + +Renderer.prototype.blockquote = function(quote) { + return '
\n' + quote + '
\n'; +}; + +Renderer.prototype.html = function(html) { + return html; +}; + +Renderer.prototype.heading = function(text, level, raw) { + return '' + + text + + '\n'; +}; + +Renderer.prototype.hr = function() { + return this.options.xhtml ? '
\n' : '
\n'; +}; + +Renderer.prototype.list = function(body, ordered) { + var type = ordered ? 'ol' : 'ul'; + return '<' + type + '>\n' + body + '\n'; +}; + +Renderer.prototype.listitem = function(text) { + return '
  • ' + text + '
  • \n'; +}; + +Renderer.prototype.paragraph = function(text) { + return '

    ' + text + '

    \n'; +}; + +Renderer.prototype.table = function(header, body) { + return '\n' + + '\n' + + header + + '\n' + + '\n' + + body + + '\n' + + '
    \n'; +}; + +Renderer.prototype.tablerow = function(content) { + return '\n' + content + '\n'; +}; + +Renderer.prototype.tablecell = function(content, flags) { + var type = flags.header ? 'th' : 'td'; + var tag = flags.align + ? '<' + type + ' style="text-align:' + flags.align + '">' + : '<' + type + '>'; + return tag + content + '\n'; +}; + +// span level renderer +Renderer.prototype.strong = function(text) { + return '' + text + ''; +}; + +Renderer.prototype.em = function(text) { + return '' + text + ''; +}; + +Renderer.prototype.codespan = function(text) { + return '' + text + ''; +}; + +Renderer.prototype.br = function() { + return this.options.xhtml ? '
    ' : '
    '; +}; + +Renderer.prototype.del = function(text) { + return '' + text + ''; +}; + +Renderer.prototype.link = function(href, title, text) { + if (this.options.sanitize) { + try { + var prot = decodeURIComponent(unescape(href)) + .replace(/[^\w:]/g, '') + .toLowerCase(); + } catch (e) { + return ''; + } + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { + return ''; + } + } + var out = '
    '; + return out; +}; + +Renderer.prototype.image = function(href, title, text) { + var out = '' + text + '' : '>'; + return out; +}; + +Renderer.prototype.text = function(text) { + return text; +}; + +/** + * Parsing & Compiling + */ + +function Parser(options) { + this.tokens = []; + this.token = null; + this.options = options || marked.defaults; + this.options.renderer = this.options.renderer || new Renderer; + this.renderer = this.options.renderer; + this.renderer.options = this.options; +} + +/** + * Static Parse Method + */ + +Parser.parse = function(src, options, renderer) { + var parser = new Parser(options, renderer); + return parser.parse(src); +}; + +/** + * Parse Loop + */ + +Parser.prototype.parse = function(src) { + this.inline = new InlineLexer(src.links, this.options, this.renderer); + this.tokens = src.reverse(); + + var out = ''; + while (this.next()) { + out += this.tok(); + } + + return out; +}; + +/** + * Next Token + */ + +Parser.prototype.next = function() { + return this.token = this.tokens.pop(); +}; + +/** + * Preview Next Token + */ + +Parser.prototype.peek = function() { + return this.tokens[this.tokens.length - 1] || 0; +}; + +/** + * Parse Text Tokens + */ + +Parser.prototype.parseText = function() { + var body = this.token.text; + + while (this.peek().type === 'text') { + body += '\n' + this.next().text; + } + + return this.inline.output(body); +}; + +/** + * Parse Current Token + */ + +Parser.prototype.tok = function() { + switch (this.token.type) { + case 'space': { + return ''; + } + case 'hr': { + return this.renderer.hr(); + } + case 'heading': { + return this.renderer.heading( + this.inline.output(this.token.text), + this.token.depth, + this.token.text); + } + case 'code': { + return this.renderer.code(this.token.text, + this.token.lang, + this.token.escaped); + } + case 'table': { + var header = '' + , body = '' + , i + , row + , cell + , flags + , j; + + // header + cell = ''; + for (i = 0; i < this.token.header.length; i++) { + flags = { header: true, align: this.token.align[i] }; + cell += this.renderer.tablecell( + this.inline.output(this.token.header[i]), + { header: true, align: this.token.align[i] } + ); + } + header += this.renderer.tablerow(cell); + + for (i = 0; i < this.token.cells.length; i++) { + row = this.token.cells[i]; + + cell = ''; + for (j = 0; j < row.length; j++) { + cell += this.renderer.tablecell( + this.inline.output(row[j]), + { header: false, align: this.token.align[j] } + ); + } + + body += this.renderer.tablerow(cell); + } + return this.renderer.table(header, body); + } + case 'blockquote_start': { + var body = ''; + + while (this.next().type !== 'blockquote_end') { + body += this.tok(); + } + + return this.renderer.blockquote(body); + } + case 'list_start': { + var body = '' + , ordered = this.token.ordered + , defn = this.token.defn; + + while (this.next().type !== 'list_end') { + body += this.tok(); + } + + return this.renderer.list(body, ordered, defn); + } + case 'list_item_start': { + var body = ''; + + while (this.next().type !== 'list_item_end') { + body += this.token.type === 'text' + ? this.parseText() + : this.tok(); + } + + return this.renderer.listitem(body); + } + case 'loose_item_start': { + var body = ''; + + while (this.next().type !== 'list_item_end') { + body += this.tok(); + } + + return this.renderer.listitem(body); + } + case 'html': { + var html = !this.token.pre && !this.options.pedantic + ? this.inline.output(this.token.text) + : this.token.text; + return this.renderer.html(html); + } + case 'paragraph': { + return this.renderer.paragraph(this.inline.output(this.token.text)); + } + case 'text': { + return this.renderer.paragraph(this.parseText()); + } + } +}; + +/** + * Helpers + */ + +function escape(html, encode) { + return html + .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function unescape(html) { + // explicitly match decimal, hex, and named HTML entities + return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) { + n = n.toLowerCase(); + if (n === 'colon') return ':'; + if (n.charAt(0) === '#') { + return n.charAt(1) === 'x' + ? String.fromCharCode(parseInt(n.substring(2), 16)) + : String.fromCharCode(+n.substring(1)); + } + return ''; + }); +} + +function replace(regex, opt) { + regex = regex.source; + opt = opt || ''; + return function self(name, val) { + if (!name) return new RegExp(regex, opt); + val = val.source || val; + val = val.replace(/(^|[^\[])\^/g, '$1'); + regex = regex.replace(name, val); + return self; + }; +} + +function noop() {} +noop.exec = noop; + +function merge(obj) { + var i = 1 + , target + , key; + + for (; i < arguments.length; i++) { + target = arguments[i]; + for (key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + obj[key] = target[key]; + } + } + } + + return obj; +} + + +/** + * Marked + */ + +function marked(src, opt, callback) { + if (callback || typeof opt === 'function') { + if (!callback) { + callback = opt; + opt = null; + } + + opt = merge({}, marked.defaults, opt || {}); + + var highlight = opt.highlight + , tokens + , pending + , i = 0; + + try { + tokens = Lexer.lex(src, opt) + } catch (e) { + return callback(e); + } + + pending = tokens.length; + + var done = function(err) { + if (err) { + opt.highlight = highlight; + return callback(err); + } + + var out; + + try { + out = Parser.parse(tokens, opt); + } catch (e) { + err = e; + } + + opt.highlight = highlight; + + return err + ? callback(err) + : callback(null, out); + }; + + if (!highlight || highlight.length < 3) { + return done(); + } + + delete opt.highlight; + + if (!pending) return done(); + + for (; i < tokens.length; i++) { + (function(token) { + if (token.type !== 'code') { + return --pending || done(); + } + return highlight(token.text, token.lang, function(err, code) { + if (err) return done(err); + if (code == null || code === token.text) { + return --pending || done(); + } + token.text = code; + token.escaped = true; + --pending || done(); + }); + })(tokens[i]); + } + + return; + } + try { + if (opt) opt = merge({}, marked.defaults, opt); + return Parser.parse(Lexer.lex(src, opt), opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/chjj/marked.'; + if ((opt || marked.defaults).silent) { + return '

    An error occured:

    '
    +        + escape(e.message + '', true)
    +        + '
    '; + } + throw e; + } +} + +/** + * Options + */ + +marked.options = +marked.setOptions = function(opt) { + merge(marked.defaults, opt); + return marked; +}; + +marked.defaults = { + gfm: true, + tables: true, + breaks: false, + pedantic: false, + sanitize: false, + sanitizer: null, + mangle: true, + smartLists: false, + silent: false, + highlight: null, + langPrefix: 'lang-', + smartypants: false, + headerPrefix: '', + renderer: new Renderer, + xhtml: false +}; + +/** + * Expose + */ + +marked.Parser = Parser; +marked.parser = Parser.parse; + +marked.Renderer = Renderer; + +marked.Lexer = Lexer; +marked.lexer = Lexer.lex; + +marked.InlineLexer = InlineLexer; +marked.inlineLexer = InlineLexer.output; + +marked.parse = marked; + +if (typeof module !== 'undefined' && typeof exports === 'object') { + module.exports = marked; +} else if (typeof define === 'function' && define.amd) { + define(function() { return marked; }); +} else { + this.marked = marked; +} + +}).call(function() { + return this || (typeof window !== 'undefined' ? window : global); +}()); \ No newline at end of file diff --git a/shared/homebrewery/phb_style/phb.blocks.less b/shared/homebrewery/phb_style/phb.blocks.less index 26d39fbc0..c85aedd78 100644 --- a/shared/homebrewery/phb_style/phb.blocks.less +++ b/shared/homebrewery/phb_style/phb.blocks.less @@ -1,15 +1,7 @@ /////////////////// .spell{ - ul:first-of-type{ - margin-top : -0.5em; - margin-bottom : 0.5em; - list-style-type : none; - &+p{ - text-indent : 0em; - } - } } .monster{ .breakAvoid(); @@ -23,10 +15,6 @@ tbody tr { background-color: transparent }; } ul:nth-of-type(1),ul:nth-of-type(2){ - list-style: none; - padding-left : 1em; - text-indent : -1em; - margin-bottom : 0.5em; strong{ color : @crimson; } @@ -36,6 +24,7 @@ right : 7px; bottom : 19px; left : 7px; + //TODO: Swap out to variable color background-color : #FDF1DC; border-image-slice : 8; border-image-source : @monsterBorder; @@ -43,6 +32,7 @@ } &.wide{ column-count : 2; + column-fill:auto; } } .note{ @@ -104,7 +94,7 @@ z-index : 150; width : 200px; font-size : 0.9em; - color : @gold; + color : @brown; text-align : right; } //***************************** @@ -161,38 +151,44 @@ } .wide{ column-span : all; - -webkit-column-span : all; - -moz-column-span : all; +} +.fullPage, .full{ + column-span : all; + height: 100%; } .oneColumn{ column-count : 1; + column-fill:auto; // column-gap : 1cm; } .twoColumn{ column-count : 2; + column-fill:auto; //column-fill: auto; ////column-gap : 1cm; } .threeColumn{ column-count : 3; + column-fill:auto; //column-gap : 1cm; } .fourColumn{ column-count : 4; //column-gap : 1cm; + column-fill:auto; } .columnSplit{ visibility : hidden; -webkit-column-break-bfore : always; break-before : column; } -.brushed{ - border-image-outset : 25px 17px; - border-image-repeat : round; - border-image-slice : 1250 1250 1250 1250; - border-image-width : 1250px; - border-image-source : url('http : //i.imgur.com/nzPYZyD.png'); -} +// .brushed{ +// border-image-outset : 25px 17px; +// border-image-repeat : round; +// border-image-slice : 1250 1250 1250 1250; +// border-image-width : 1250px; +// border-image-source : url('http://i.imgur.com/nzPYZyD.png'); +// } //basics .left{ text-align : left; @@ -208,4 +204,13 @@ } .sansSerif{ .useSansSerif(); +} +.dropcap{ + p::first-letter{ + float : left; + font-family : Solbera; + font-size : 10em; + color : #222; + line-height : 0.8em; + } } \ No newline at end of file diff --git a/shared/homebrewery/phb_style/phb.colors.less b/shared/homebrewery/phb_style/phb.colors.less index 35f82f24f..42abcb926 100644 --- a/shared/homebrewery/phb_style/phb.colors.less +++ b/shared/homebrewery/phb_style/phb.colors.less @@ -1,5 +1,13 @@ //TODO: come up with fun color names +@crimson : #58180D; +@red : #9c2b1b; +@brown : #c9ad6a; //brown? +@green : #e0e5c1; +@yellow : #faf7ea; //same as background? +@teal : blue; +@blue : blue; + @background : #EEE5CE; @noteGreen : #e0e5c1; @headerUnderline : #c9ad6a; @@ -20,13 +28,7 @@ } } -@crimson : #58180D; -@red : #9c2b1b; -@gold : #c9ad6a; //brown? -@green : #e0e5c1; -@yellow : #faf7ea; //same as background? -@teal : blue; -@blue : blue; + //TODO make a color mixin generator @@ -34,5 +36,6 @@ .blue{ .colorElements(@blue); } .green{ .colorElements(@green); } .yellow{ .colorElements(@yellow); } -.gold{ .colorElements(@gold); } -.red{ .colorElements(@red); } +.brown{ .colorElements(fade(@brown, 50%)); } +.red{ .colorElements(fade(@red, 25%)); } +.crimson{ .colorElements(fade(@crimson, 20%)); } diff --git a/shared/homebrewery/phb_style/phb.elements.less b/shared/homebrewery/phb_style/phb.elements.less index bebe734d2..237511973 100644 --- a/shared/homebrewery/phb_style/phb.elements.less +++ b/shared/homebrewery/phb_style/phb.elements.less @@ -1,15 +1,14 @@ + pre{ - font-family : monospace; - background-color : @yellow; - padding : 12px; - border: 1px solid #bfbfbf; - white-space: pre-wrap; - color : #333; + padding : 12px; + background-color : @yellow; + font-family : monospace; + color : #333; + border : 1px solid #bfbfbf; + white-space : pre-wrap; -webkit-column-break-inside : avoid; column-break-inside : avoid; } - - hr{ visibility : visible; height : 6px; @@ -18,8 +17,6 @@ hr{ background-size : 100% 100%; border : none; } - - p{ padding-bottom : 0.8em; line-height : 1.3em; @@ -27,35 +24,42 @@ p{ margin-top : -0.8em; } } - blockquote{ font-style : italic; &>p{ - line-height: 1.8em; + line-height : 1.8em; + //Why did I do this? &:first-child::first-line{ - + font-family : ScalySansSmallCaps; //TODO: Find the right font for block quotes - font-style: normal; - font-family: ScalySansSmallCaps; - - + font-style : normal; } } .cite{ - font-style: normal; - text-align: right; + font-style : normal; + text-align : right; + } +} +// IMAGE +img{ + display : block; + z-index : -1; + mix-blend-mode : multiply; + &.center{ + margin-right : auto; + margin-left : auto; + } + &.noBlend{ + mix-blend-mode : normal; + } + &.bg, &.background{ + position : absolute; } - - } - //Indents after p or lists p+p, ul+p, ol+p{ text-indent : 1em; } -img{ - z-index : -1; -} strong{ font-weight : bold; letter-spacing : 0.03em; @@ -88,13 +92,6 @@ h1{ font-size : 0.987cm; -webkit-column-span : all; -moz-column-span : all; - &+p::first-letter{ - float : left; - font-family : Solbera; - font-size : 10em; - color : #222; - line-height : 0.8em; - } } h2{ font-size : 0.705cm; @@ -113,8 +110,6 @@ h5{ font-size : 0.423cm; font-weight : 900; } - - //****************************** // LISTS //****************************** @@ -141,7 +136,19 @@ ol{ list-style-type : decimal; } - +p+ul.alt{ + margin-top : -0.5em; +} +ul.alt{ + margin-bottom : 0.5em; + padding-left : 0em; + list-style-position : outside; + list-style-type : none; + list-style-type : none; + &+p{ + text-indent : 0em; + } +} //***************************** // * TABLE // *****************************/ @@ -150,6 +157,7 @@ table{ width : 100%; margin-bottom : 1em; font-size : 10pt; + break-inside : avoid-column; thead{ font-weight : 800; th{ diff --git a/shared/homebrewery/phb_style/phb.less b/shared/homebrewery/phb_style/phb.less index e9adf63c6..9f739f2f5 100644 --- a/shared/homebrewery/phb_style/phb.less +++ b/shared/homebrewery/phb_style/phb.less @@ -57,7 +57,7 @@ background-size : cover; padding: 28px 63px; box-sizing: border-box; - color : lighten(@gold, 0%); + color : lighten(@brown, 0%); font-size: 0.7em; } &:nth-child(even){ @@ -81,7 +81,7 @@ bottom : 22px; width : 50px; font-size : 0.9em; - color : @gold; + color : @brown; text-align : center; } diff --git a/shared/homebrewery/snippets/brew/brew.snippet.js b/shared/homebrewery/snippets/brew/brew.snippet.js new file mode 100644 index 000000000..141d831b8 --- /dev/null +++ b/shared/homebrewery/snippets/brew/brew.snippet.js @@ -0,0 +1,74 @@ +const _ = require('lodash'); +const Data = require('./random.data.js'); + + +const Snips = _.merge( + require('./spell.snippet.js'), + require('./table.snippet.js'), + require('./class.snippet.js'), + require('./note.snippet.js'), + require('./monster.snippet.js') +); + +const BrewSnippets = { + brewModel : ()=>{ + return { + title : Data.rand(Data.titles), + description : Data.rand(Data.subtitles), + text : BrewSnippets.brew(), + + authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), + systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), + views : _.random(0,1000), + published : !!_.random(0,1) + } + }, + brew : ()=>{ + return _.times(_.random(1,10), ()=>_.sample([ + BrewSnippets.page1, + BrewSnippets.page2 + ])()).join('\n\n\\page\n\n'); + }, + + page1 : ()=>{ + return BrewSnippets.filler(_.random(10, 15), [Snips.monster, Snips.table]); + }, + + page2 : ()=>{ + const title = '# ' + Data.rand('titles'); + let table = Snips.noncasterTable(); + + if(Data.chance(3)){ + table = Snips.casterTable(); + if(Data.chance(2)) table = Snips.halfcasterTable(); + return `${title}\n\n${table}\n\n${BrewSnippets.paragraph(true)}\n\n${BrewSnippets.filler(3)}`; + } + return `${title}\n\n${table}\n\n${BrewSnippets.paragraph(true)}\n\n${BrewSnippets.filler(5)}`; + }, + + filler : (count = 1, additional = [])=>{ + const base = _.concat([ + BrewSnippets.paragraph, BrewSnippets.paragraph, BrewSnippets.paragraph, BrewSnippets.paragraph, + BrewSnippets.paragraph, BrewSnippets.paragraph, BrewSnippets.paragraph, BrewSnippets.paragraph, + Snips.table, + Snips.note, Snips.note, Snips.altnote + ], additional); + return _.times(count, ()=>{ + let res = _.sample(base)(); + if(Data.chance(8)) res = BrewSnippets.title() + '\n\n' + res; + return res; + }).join('\n\n'); + }, + + paragraph : (dropcap = false)=>{ + let res = Data.rand(Data.sentences, 6, 3).join(' '); + if(dropcap || Data.chance(20)) res = `{{dropcap\n${res}\n}}`; + return res; + }, + + title : ()=>{ + return _.sample(['##', '###', '##', '###','####', '#####']) + ' ' + Data.rand('titles'); + } +} + +module.exports = BrewSnippets; \ No newline at end of file diff --git a/shared/homebrewery/snippets/brew/class.snippet.js b/shared/homebrewery/snippets/brew/class.snippet.js index 9e84757f4..879d823b0 100644 --- a/shared/homebrewery/snippets/brew/class.snippet.js +++ b/shared/homebrewery/snippets/brew/class.snippet.js @@ -11,6 +11,52 @@ const getFeature = (level)=>{ return res.join(', '); }; +const spellLevels = [ + [2,'―','―','―','―','―','―','―','―'], + [3,'―','―','―','―','―','―','―','―'], + [4, 2 ,'―','―','―','―','―','―','―'], + [4, 3 ,'―','―','―','―','―','―','―'], + [4, 3 , 2 ,'―','―','―','―','―','―'], + [4, 3 , 3 ,'―','―','―','―','―','―'], + [4, 3 , 3 , 1 ,'―','―','―','―','―'], + [4, 3 , 3 , 2 ,'―','―','―','―','―'], + [4, 3 , 3 , 3 , 1 ,'―','―','―','―'], + [4, 3 , 3 , 3 , 2 ,'―','―','―','―'], + [4, 3 , 3 , 3 , 2 , 1 ,'―','―','―'], + [4, 3 , 3 , 3 , 2 , 1 ,'―','―','―'], + [4, 3 , 3 , 3 , 2 , 1 , 1 ,'―','―'], + [4, 3 , 3 , 3 , 2 , 1 , 1 ,'―','―'], + [4, 3 , 3 , 3 , 2 , 1 , 1 , 1 ,'―'], + [4, 3 , 3 , 3 , 2 , 1 , 1 , 1 ,'―'], + [4, 3 , 3 , 3 , 2 , 1 , 1 , 1 , 1 ], + [4, 3 , 3 , 3 , 3 , 1 , 1 , 1 , 1 ], + [4, 3 , 3 , 3 , 3 , 2 , 1 , 1 , 1 ], + [4, 3 , 3 , 3 , 3 , 2 , 2 , 1 , 1 ] +]; + +const halfspellLevels = [ + ['―','―','―','―','―'], + [ 2 ,'―','―','―','―'], + [ 3 ,'―','―','―','―'], + [ 3 ,'―','―','―','―'], + [ 4 , 2 ,'―','―','―'], + [ 4 , 2 ,'―','―','―'], + [ 4 , 3 ,'―','―','―'], + [ 4 , 3 ,'―','―','―'], + [ 4 , 3 , 2 ,'―','―'], + [ 4 , 3 , 2 ,'―','―'], + [ 4 , 3 , 3 ,'―','―'], + [ 4 , 3 , 3 ,'―','―'], + [ 4 , 3 , 3 , 1 ,'―'], + [ 4 , 3 , 3 , 1 ,'―'], + [ 4 , 3 , 3 , 2 ,'―'], + [ 4 , 3 , 3 , 2 ,'―'], + [ 4 , 3 , 3 , 3 , 1 ], + [ 4 , 3 , 3 , 3 , 1 ], + [ 4 , 3 , 3 , 3 , 2 ], + [ 4 , 3 , 3 , 3 , 2 ] +]; + module.exports = { @@ -23,13 +69,15 @@ module.exports = { lvlText, '+'+Math.floor(level/4 + 2), getFeature(level), - '+'+featureScore - ].join(' | ') + ' |'; + '??' + ].join(' | ') + ' | ' + + spellLevels[level].join(' | ') + + ' |'; }).join('\n'); return `{{frame,wide ##### ${Data.rand('classes')} -| Level | Proficiency Bonus | Features | Cantrips Known | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th | +| Level | Proficiency Bonus | Features | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th | |:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| ${rows} }}`; @@ -43,9 +91,10 @@ ${rows} return '| ' + [ lvlText, '+'+Math.floor(level/4 + 2), - getFeature(level), - '+'+featureScore - ].join(' | ') + ' |'; + getFeature(level) + ].join(' | ') + ' | ' + + halfspellLevels[level].join(' | ') + + ' |' }).join('\n'); diff --git a/shared/homebrewery/snippets/brew/index.js b/shared/homebrewery/snippets/brew/index.js index 4671d6d37..0e91b61e0 100644 --- a/shared/homebrewery/snippets/brew/index.js +++ b/shared/homebrewery/snippets/brew/index.js @@ -8,7 +8,8 @@ module.exports = _.merge( require('./monster.snippet.js'), require('./toc.snippet.js'), - require('./random.brew.js') + //require('./random.brew.js') + require('./brew.snippet.js') diff --git a/shared/homebrewery/snippets/brew/monster.snippet.js b/shared/homebrewery/snippets/brew/monster.snippet.js index ad41335a4..70447d6cf 100644 --- a/shared/homebrewery/snippets/brew/monster.snippet.js +++ b/shared/homebrewery/snippets/brew/monster.snippet.js @@ -12,13 +12,10 @@ const getStats = function(){ const getAttributes = ()=>{ - - - return ` - **Saving Throws** -- **Condition Immunities** " + genList(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3), -- **Senses** passive Perception " + _.random(3, 20), +- **Condition Immunities** ${Data.rand(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3).join(', ')}, +- **Senses** passive Perception " ${_.random(3, 20)}, - **Languages** ${Data.rand(["Common", "Pottymouth", "Gibberish", "Latin", "Jive"], 2).join(', ')} - **Challenge** ${_.random(0, 15)} (${_.random(10,10000)} XP) `; @@ -45,23 +42,16 @@ module.exports = { *${Data.rand('sizes')}, ${Data.rand('alignments')}* --- - - **Armor Class** ${_.random(10,20)} - **Hit Points** ${_.random(1, 150)} (1d4 + 5) - **Speed** ${ _.random(0,50)} ft - --- - |STR|DEX|CON|INT|WIS|CHA| |:---:|:---:|:---:|:---:|:---:|:---:| ${getStats()} - --- - ${getAttributes()} - --- - Abilities diff --git a/shared/homebrewery/snippets/brew/note.snippet.js b/shared/homebrewery/snippets/brew/note.snippet.js index 18c9233b1..2b79f6938 100644 --- a/shared/homebrewery/snippets/brew/note.snippet.js +++ b/shared/homebrewery/snippets/brew/note.snippet.js @@ -4,7 +4,7 @@ const Data = require('./random.data.js'); module.exports = { note : ()=>{ - return `{{note + return `{{note,red ##### ${Data.rand('abilities')} ${Data.rand('sentences', 6, 4).join(' ')} }}` diff --git a/shared/homebrewery/snippets/brew/random.data.js b/shared/homebrewery/snippets/brew/random.data.js index d360a2d80..9b9493302 100644 --- a/shared/homebrewery/snippets/brew/random.data.js +++ b/shared/homebrewery/snippets/brew/random.data.js @@ -5,7 +5,9 @@ const Data = { const data = (Data[name] ? Data[name] : name); return _.sampleSize(data, _.random(min, max)); }, - + //Boolean of 1 in n chance + chance : (max = 20)=>_.random(1,max)== 1, + mix : (list, max = 1, min = 1)=>_.times(_.random(min, max), ()=>_.sample(list)), titles : [ `The Burning Gallows`, `The Ring of Nenlast`, diff --git a/shared/homebrewery/snippets/brew/spell.snippet.js b/shared/homebrewery/snippets/brew/spell.snippet.js index 658dfc176..6f0365f67 100644 --- a/shared/homebrewery/snippets/brew/spell.snippet.js +++ b/shared/homebrewery/snippets/brew/spell.snippet.js @@ -26,7 +26,7 @@ module.exports = { const description = Data.rand('effects', 2).concat(Data.rand('effects2')).join(' '); - return `{{spell + return ` #### ${_.sample(Data.spellNames)} *${_.sample(levels)}-level ${_.sample(schools)}* - **Casting Time:** ${_.sample(['1 action', 'Reaction', '10 minutes', '1 hour'])} @@ -34,8 +34,18 @@ module.exports = { - **Components:** ${components} - **Duration:** ${duration} -${description} -}}`; +${description}`; + }, + + spellList : ()=>{ + const levels = ['Cantrips (0 Level)', '2nd Level', '3rd Level', '4th Level', '5th Level', '6th Level', '7th Level', '8th Level', '9th Level']; + + const content = _.map(levels, (level)=>{ + const spells = _.map(Data.rand('spellNames', 15, 5), (spell)=>`- ${spell}`).join('\n'); + return `##### ${level} \n${spells} \n`; + }).join('\n'); + + return `{{fourColumn,fullPage,sansSerif\n${content}\n}}`; } } \ No newline at end of file diff --git a/shared/homebrewery/snippets/brew/table.snippet.js b/shared/homebrewery/snippets/brew/table.snippet.js index 00ad1ffd9..8f195d544 100644 --- a/shared/homebrewery/snippets/brew/table.snippet.js +++ b/shared/homebrewery/snippets/brew/table.snippet.js @@ -43,15 +43,26 @@ const columns = { module.exports = { table : () => { + let title = ''; + + if(Data.chance(5)) title = `##### ${Data.rand(Data.abilities)}\n`; + + const rows = _.sample([4,6,8,10]); - const cols = [ - columns.roll(rows), - columns.level(rows), - columns.gear(rows) - ]; + let fns = []; + if(Data.chance(3)) fns.push(columns.roll); - return _.times(rows + 2, (i)=>{ + fns = _.concat(fns, Data.rand([ + columns.level, + columns.spell, + columns.cost, + columns.gear + ], 3, 2 - fns.length)) + + const cols = _.map(fns, (fn)=>fn(rows)); + + return title + _.times(rows + 2, (i)=>{ if(i==1){ return '|' + _.map(cols, (col)=>col[i]).join('|') + '|'; }else{