"use strict";
class RendererCard {
constructor () {
// FIXME this is awful
const renderer = new Renderer();
for (const k in renderer) {
if (this[k] === undefined) {
if (typeof renderer[k] === "function") this[k] = renderer[k].bind(this);
else this[k] = MiscUtil.copy(renderer[k]);
}
}
}
static get () {
return new RendererCard().setFnPostProcess(RendererCard._fnPostProcess);
}
static _fnPostProcess (str) {
return str.replace(/\n\n+/g, "\n\n");
}
// region recursive
/*
_renderEntries (entry, textStack, meta, options) {
// (Use base implementation)
}
*/
_renderEntriesSubtypes (entry, textStack, meta, options, incDepth) {
const isInlineTitle = meta.depth >= 2;
const nextDepth = incDepth && meta.depth < 2 ? meta.depth + 1 : meta.depth;
if (entry.name) {
if (isInlineTitle) {
textStack[0] += `description | ${Renderer.stripTags(entry.name)} | `;
if (entry.entries) {
const cacheDepth = meta.depth;
meta.depth = nextDepth;
this._recursiveRender(entry.entries[0], textStack, meta, {suffix: "\n"});
meta.depth = cacheDepth;
}
} else {
textStack[0] += `section | ${Renderer.stripTags(entry.name)}\n`;
}
}
if (entry.entries) {
this._renderEntriesSubtypes_renderPreReqText(entry, textStack, meta);
const cacheDepth = meta.depth;
const len = entry.entries.length;
for (let i = entry.name && isInlineTitle ? 1 : 0; i < len; ++i) {
meta.depth = nextDepth;
this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: "\n"});
}
meta.depth = cacheDepth;
}
}
_renderEntriesSubtypes_renderPreReqText (entry, textStack, meta) {
if (entry.prerequisite) {
textStack[0] += `text | Prerequisite: `;
this._recursiveRender({type: "inline", entries: [entry.prerequisite]}, textStack, meta);
textStack[0] += `\n`;
}
}
/*
_renderOptions (entry, textStack, meta, options) {
// (Use base implementation)
}
*/
_renderList (entry, textStack, meta, options) {
if (!entry.items) return;
if (entry.name) textStack[0] += `text | ${entry.name}\n`;
const len = entry.items.length;
for (let i = 0; i < len; ++i) {
const cacheDepth = this._adjustDepth(meta, 1);
const item = entry.items[i];
if (item.type === "itemSpell" || item.type === "itemSub") {
this._recursiveRender(item, textStack, meta);
} else this._recursiveRender(item, textStack, meta, {prefix: `bullet | `, suffix: "\n"});
meta.depth = cacheDepth;
}
}
_renderTable (entry, textStack, meta, options) {
const _VERTICAL_LINE = "|"; // "FULLWIDTH VERTICAL LINE" character
if (entry.intro) for (const ent of entry.intro) this._recursiveRender(ent, textStack, meta, {prefix: "text | ", suffix: "\n"});
textStack[0] += "\n";
if (entry.caption) textStack[0] += `text | ${entry.caption}\n`;
const headerRowMetas = Renderer.table.getHeaderRowMetas(entry);
if (headerRowMetas) {
for (let ixRow = 0; ixRow < headerRowMetas.length; ++ixRow) {
textStack[0] += `text | `;
const headerRowMeta = headerRowMetas[ixRow];
for (let ixCell = 0; ixCell < headerRowMeta.length; ++ixCell) {
const label = headerRowMeta[ixCell];
this._recursiveRender(label, textStack, meta);
if (ixCell !== headerRowMeta.length - 1) textStack[0] += _VERTICAL_LINE;
}
textStack[0] += `\n`;
}
}
if (!entry.rows || !entry.rows.length) return;
for (const row of entry.rows) {
textStack[0] += "text | ";
const rowRender = row.type === "row" ? row.row : row;
for (let i = 0; i < rowRender.length; ++i) {
const cell = rowRender[i];
let toRenderCell;
if (cell.type === "cell") {
if (cell.roll) {
if (cell.roll.entry) toRenderCell = cell.roll.entry;
else if (cell.roll.exact != null) toRenderCell = cell.roll.pad ? StrUtil.padNumber(cell.roll.exact, 2, "0") : cell.roll.exact;
else {
toRenderCell = cell.roll.pad
? `${StrUtil.padNumber(cell.roll.min, 2, "0")}-${StrUtil.padNumber(cell.roll.max, 2, "0")}`
: `${cell.roll.min}-${cell.roll.max}`;
}
} else if (cell.entry) {
toRenderCell = cell.entry;
}
} else {
toRenderCell = cell;
}
const cacheDepth = this._adjustDepth(meta, 1);
this._recursiveRender(toRenderCell, textStack, meta);
meta.depth = cacheDepth;
if (i !== rowRender.length - 1) textStack[0] += _VERTICAL_LINE;
}
textStack[0] += "\n";
}
if (entry.footnotes) {
for (const ent of entry.footnotes) {
const cacheDepth = this._adjustDepth(meta, 1);
this._recursiveRender(ent, textStack, meta);
meta.depth = cacheDepth;
}
}
if (entry.outro) for (const ent of entry.intro) this._recursiveRender(ent, textStack, meta, {prefix: "text | ", suffix: "\n"});
}
/*
_renderTableGroup (entry, textStack, meta, options) {
// (Use base implementation)
}
*/
_renderInset (entry, textStack, meta, options) {
textStack[0] += "\n";
if (entry.name != null) textStack[0] += `section | ${entry.name}\n`;
if (entry.entries) {
const len = entry.entries.length;
for (let i = 0; i < len; ++i) {
const cacheDepth = meta.depth;
meta.depth = 2;
this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: "\n"});
meta.depth = cacheDepth;
}
}
textStack[0] += `\n`;
}
_renderInsetReadaloud (entry, textStack, meta, options) {
this._renderInset(entry, textStack, meta, options);
}
_renderVariant (entry, textStack, meta, options) {
textStack[0] += "\n";
if (entry.name != null) textStack[0] += `section | Variant: ${entry.name}\n`;
if (entry.entries) {
const len = entry.entries.length;
for (let i = 0; i < len; ++i) {
const cacheDepth = meta.depth;
meta.depth = 2;
this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: "\n"});
meta.depth = cacheDepth;
}
}
if (entry.source) textStack[0] += `${RenderCard.utils.getPageText({source: entry.source, page: entry.page})}\n`;
textStack[0] += "\n";
}
_renderVariantSub (entry, textStack, meta, options) {
if (entry.name) {
textStack[0] += `text | ${entry.name}. `;
if (entry.entries) {
this._recursiveRender(entry.entries[0], textStack, meta, {suffix: "\n"});
}
}
if (entry.entries) {
const len = entry.entries.length;
for (let i = entry.name ? 1 : 0; i < len; ++i) {
this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: "\n"});
}
}
}
_renderSpellcasting (entry, textStack, meta, options) {
const toRender = this._renderSpellcasting_getEntries(entry);
this._recursiveRender({type: "entries", entries: toRender}, textStack, meta, {prefix: "text | ", suffix: "\n"});
}
_renderQuote (entry, textStack, meta, options) {
const len = entry.entries.length;
for (let i = 0; i < len; ++i) {
this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: ""});
textStack[0] += `\n`;
}
const byArr = this._renderQuote_getBy(entry);
if (byArr) {
const tempStack = [""];
for (let i = 0, len = byArr.length; i < len; ++i) {
const by = byArr[i];
tempStack[0] += `text | ${!i ? `\u2014 ` : ""}`;
this._recursiveRender(by, tempStack, meta);
if (i < len - 1) tempStack[0] += "\n";
}
textStack[0] += `${tempStack.join("")}${entry.from ? `, ${entry.from}` : ""}\n`;
}
}
/*
_renderOptfeature (entry, textStack, meta, options) {
// (Use base implementation)
}
_renderPatron (entry, textStack, meta, options) {
// (Use base implementation)
}
// endregion
*/
// region block
_renderAbilityDc (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
textStack[0] += `${entry.name} save DC = 8 + your proficiency bonus + your ${Parser.attrChooseToFull(entry.attributes)}`;
this._renderSuffix(entry, textStack, meta, options);
}
_renderAbilityAttackMod (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
textStack[0] += `${entry.name} attack modifier = your proficiency bonus + your ${Parser.attrChooseToFull(entry.attributes)}`;
this._renderSuffix(entry, textStack, meta, options);
}
_renderAbilityGeneric (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
textStack[0] += `${entry.name ? `${entry.name} = ` : ""}${entry.text}${entry.attributes ? ` ${Parser.attrChooseToFull(entry.attributes)}` : ""}`;
this._renderSuffix(entry, textStack, meta, options);
}
// endregion
/*
// region inline
_renderInline (entry, textStack, meta, options) {
// (Use base implementation)
}
_renderInlineBlock (entry, textStack, meta, options) {
// (Use base implementation)
}
_renderBonus (entry, textStack, meta, options) {
// (Use base implementation)
}
_renderBonusSpeed (entry, textStack, meta, options) {
// (Use base implementation)
}
*/
_renderDice (entry, textStack, meta, options) {
textStack[0] += Renderer.getEntryDiceDisplayText(entry);
}
_renderLink (entry, textStack, meta, options) {
this._recursiveRender(entry.text, textStack, meta);
}
/*
_renderActions (entry, textStack, meta, options) {
// TODO?
}
_renderAttack (entry, textStack, meta, options) {
// TODO?
}
_renderIngredient (entry, textStack, meta, options) {
// (Use base implementation)
}
// endregion
*/
// region list items
_renderItem (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
textStack[0] += `${this.render(entry.name)} `;
if (entry.entry) this._recursiveRender(entry.entry, textStack, meta);
else if (entry.entries) {
const len = entry.entries.length;
for (let i = 0; i < len; ++i) {
if (i === 0) this._recursiveRender(entry.entries[i], textStack, meta, {suffix: "\n"});
else this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: "\n"});
}
}
textStack[0] += "\n";
this._renderSuffix(entry, textStack, meta, options);
}
_renderItemSub (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
this._recursiveRender(entry.entry, textStack, meta, {prefix: `bullet | ${this.render(entry.name)} `, suffix: "\n"});
this._renderSuffix(entry, textStack, meta, options);
}
_renderItemSpell (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
this._recursiveRender(entry.entry, textStack, meta, {prefix: `bullet | ${entry.name} `, suffix: "\n"});
this._renderSuffix(entry, textStack, meta, options);
}
// endregion
// region embedded entities
_renderStatblockInline (entry, textStack, meta, options) {
textStack[0] += `text | (Inline stat block rendering within cards is not supported.)\n`;
}
_renderStatblock (entry, textStack, meta, options) {
textStack[0] += `text | (Inline stat block rendering within cards is not supported.)\n`;
}
// endregion
// region images
_renderImage (entry, textStack, meta, options) {
textStack[0] += `text | (Image rendering within cards is not supported.)\n`;
}
_renderGallery (entry, textStack, meta, options) {
textStack[0] += `text | (Image gallery rendering within cards is not supported.)\n`;
}
// endregion
// region flowchart
_renderFlowchart (entry, textStack, meta, options) {
const len = entry.blocks.length;
for (let i = 0; i < len; ++i) this._recursiveRender(entry.blocks[i], textStack, meta, options);
}
_renderFlowBlock (entry, textStack, meta, options) {
textStack[0] += "\n";
if (entry.name != null) textStack[0] += `section | ${entry.name}\n`;
if (entry.entries) {
const len = entry.entries.length;
for (let i = 0; i < len; ++i) {
const cacheDepth = meta.depth;
meta.depth = 2;
this._recursiveRender(entry.entries[i], textStack, meta, {prefix: "text | ", suffix: "\n"});
meta.depth = cacheDepth;
}
}
textStack[0] += `\n`;
}
// endregion
// region homebrew
_renderHomebrew (entry, textStack, meta, options) {
textStack[0] += `text | (Homebrew rendering within cards is not supported.)\n`;
}
// endregion
// region misc
_renderCode (entry, textStack, meta, options) {
textStack[0] += `text | (Code rendering within cards is not supported.)`;
}
_renderHr (entry, textStack, meta, options) {
textStack[0] += `rule\n`;
}
// endregion
// region primitives
_renderString (entry, textStack, meta, options) {
const tagSplit = Renderer.splitByTags(entry);
const len = tagSplit.length;
for (let i = 0; i < len; ++i) {
const s = tagSplit[i];
if (!s) continue;
if (s.startsWith("{@")) {
const [tag, text] = Renderer.splitFirstSpace(s.slice(1, -1));
if (!this._renderString_renderTag_card(textStack, meta, options, tag, text)) this._renderString_renderTag(textStack, meta, options, tag, text);
} else {
if (textStack[0].last() === "\n" || !textStack[0].last()) textStack[0] += `text | `;
textStack[0] += s;
}
}
}
_renderString_renderTag_card (textStack, meta, options, tag, text) {
switch (tag) {
case "@dc": {
const [dcText, displayText] = Renderer.splitTagByPipe(text);
textStack[0] += `DC ${displayText || dcText}`;
return true;
}
default: return false;
}
}
_renderPrimitive (entry, textStack, meta, options) {
if (textStack[0].last() === "\n" || !textStack[0].last()) textStack[0] += `text | `;
textStack[0] += `${entry}`;
}
// endregion
}
RendererCard.utils = class {
static getPageText (it) {
const sourceSub = Renderer.utils.getSourceSubText(it);
const baseText = Renderer.utils.isDisplayPage(it.page) ? `text | Source: ${Parser.sourceJsonToAbv(it.source)}${sourceSub}, page ${it.page}` : "";
const addSourceText = this._getPageText_getAltSourceText(it, "additionalSources", "Additional information from");
const otherSourceText = this._getPageText_getAltSourceText(it, "otherSources", "Also found in");
const externalSourceText = this._getPageText_getAltSourceText(it, "externalSources", "External sources:");
return `${[baseText, addSourceText, otherSourceText, externalSourceText].filter(it => it).join(". ")}${baseText && (addSourceText || otherSourceText || externalSourceText) ? "." : ""}\n`;
}
static _getPageText_getAltSourceText (it, prop, introText) {
if (!it[prop] || !it[prop].length) return "";
return `${introText} ${it[prop].map(as => {
if (as.entry) return Renderer.get().render(as.entry);
else return `${Parser.sourceJsonToAbv(as.source)}>${Renderer.utils.isDisplayPage(as.page) ? `, page ${as.page}` : ""}`;
}).join("; ")}`;
}
};