This commit is contained in:
TheGiddyLimit
2024-03-10 21:53:34 +00:00
parent b323d4123e
commit f00d1f3833
272 changed files with 24017 additions and 9350 deletions

View File

@@ -5,12 +5,12 @@ class ActionsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-8 pl-0",
css: "bold ve-col-8 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Time",
css: "ve-text-center col-4 pr-0",
css: "ve-text-center ve-col-4 pr-0",
colStyle: "text-center",
}),
];
@@ -72,10 +72,10 @@ class ActionsPage extends ListPage {
const time = it.time ? it.time.map(tm => PageFilterActions.getTimeText(tm)).join("/") : "\u2014";
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="col-5-7 px-1 bold">${it.name}</span>
<span class="col-4 ve-text-center">${time}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="ve-col-5-7 px-1 bold">${it.name}</span>
<span class="ve-col-4 ve-text-center">${time}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>
<div class="ve-flex ve-hidden relative lst__wrp-preview">
<div class="vr-0 absolute lst__vr-preview"></div>

View File

@@ -20,11 +20,11 @@ class AdventuresList extends AdventuresBooksList {
rootPage: "adventure.html",
rowBuilderFn: (adv) => {
return `
<span class="col-1-3 ve-text-center mobile__text-clip-ellipsis">${AdventuresBooksList._getGroupStr(adv)}</span>
<span class="col-5-5 bold mobile__text-clip-ellipsis">${adv.name}</span>
<span class="col-2-5 mobile__text-clip-ellipsis">${adv.storyline || "\u2014"}</span>
<span class="col-1 ve-text-center mobile__text-clip-ellipsis">${AdventuresList._getLevelsStr(adv)}</span>
<span class="col-1-7 ve-text-center mobile__text-clip-ellipsis code">${AdventuresBooksList._getDateStr(adv)}</span>
<span class="ve-col-1-3 ve-text-center mobile__text-clip-ellipsis">${AdventuresBooksList._getGroupStr(adv)}</span>
<span class="ve-col-5-5 bold mobile__text-clip-ellipsis">${adv.name}</span>
<span class="ve-col-2-5 mobile__text-clip-ellipsis">${adv.storyline || "\u2014"}</span>
<span class="ve-col-1 ve-text-center mobile__text-clip-ellipsis">${AdventuresList._getLevelsStr(adv)}</span>
<span class="ve-col-1-7 ve-text-center mobile__text-clip-ellipsis code">${AdventuresBooksList._getDateStr(adv)}</span>
`;
},
});

View File

@@ -5,12 +5,12 @@ class BackgroundSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-4 pl-0",
css: "bold ve-col-4 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Skills",
css: "col-8 pr-0",
css: "ve-col-8 pr-0",
colStyle: "",
}),
];
@@ -80,9 +80,9 @@ class BackgroundPage extends ListPage {
const source = Parser.sourceJsonToAbv(bg.source);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-4 pl-0">${name}</span>
<span class="col-6">${bg._skillDisplay}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(bg.source)} pr-0" title="${Parser.sourceJsonToFull(bg.source)}" ${Parser.sourceJsonToStyle(bg.source)}>${source}</span>
<span class="bold ve-col-4 pl-0">${name}</span>
<span class="ve-col-6">${bg._skillDisplay}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(bg.source)} pr-0" title="${Parser.sourceJsonToFull(bg.source)}" ${Parser.sourceJsonToStyle(bg.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -68,22 +68,22 @@ class BestiarySublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-5 pl-0",
css: "bold ve-col-5 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Type",
css: "col-3-8",
css: "ve-col-3-8",
colStyle: "",
}),
new SublistCellTemplate({
name: "CR",
css: "col-1-2 ve-text-center",
css: "ve-col-1-2 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Number",
css: "col-2 ve-text-center",
css: "ve-col-2 ve-text-center",
colStyle: "text-center",
}),
];
@@ -98,7 +98,7 @@ class BestiarySublistManager extends SublistManager {
const cellsText = [name, type, cr];
const $hovStatblock = $(`<span class="col-1-4 help help--hover best-ecgen__visible">Stat Block</span>`)
const $hovStatblock = $(`<span class="ve-col-1-4 help help--hover best-ecgen__visible">Stat Block</span>`)
.mouseover(evt => this._encounterBuilder.doStatblockMouseOver({
evt,
ele: $hovStatblock[0],
@@ -110,26 +110,26 @@ class BestiarySublistManager extends SublistManager {
.mouseleave(evt => Renderer.hover.handleLinkMouseLeave(evt, $hovStatblock[0]));
const hovTokenMeta = EncounterBuilderUiBestiary.getTokenHoverMeta(mon);
const $hovToken = !hovTokenMeta ? $(`<span class="col-1-2 best-ecgen__visible"></span>`) : $(`<span class="col-1-2 best-ecgen__visible help help--hover">Token</span>`)
const $hovToken = !hovTokenMeta ? $(`<span class="ve-col-1-2 best-ecgen__visible"></span>`) : $(`<span class="ve-col-1-2 best-ecgen__visible help help--hover">Token</span>`)
.mouseover(evt => hovTokenMeta.mouseOver(evt, $hovToken[0]))
.mousemove(evt => hovTokenMeta.mouseMove(evt, $hovToken[0]))
.mouseleave(evt => hovTokenMeta.mouseLeave(evt, $hovToken[0]));
const $hovImage = $(`<span class="col-1-2 best-ecgen__visible help help--hover">Image</span>`);
const $hovImage = $(`<span class="ve-col-1-2 best-ecgen__visible help help--hover">Image</span>`);
Renderer.monster.hover.bindFluffImageMouseover({mon, $ele: $hovImage});
const $ptCr = (() => {
if (!ScaleCreature.isCrInScaleRange(mon)) return $(`<span class="col-1-2 ve-text-center">${cr}</span>`);
if (!ScaleCreature.isCrInScaleRange(mon)) return $(`<span class="ve-col-1-2 ve-text-center">${cr}</span>`);
const $iptCr = $(`<input value="${cr}" class="w-100 ve-text-center form-control form-control--minimal input-xs">`)
.click(() => $iptCr.select())
.change(() => this._encounterBuilder.pDoCrChange($iptCr, mon, mon._scaledCr));
return $$`<span class="col-1-2 ve-text-center">${$iptCr}</span>`;
return $$`<span class="ve-col-1-2 ve-text-center">${$iptCr}</span>`;
})();
const $eleCount1 = $(`<span class="col-2 ve-text-center">${count}</span>`);
const $eleCount2 = $(`<span class="col-2 pr-0 ve-text-center">${count}</span>`);
const $eleCount1 = $(`<span class="ve-col-2 ve-text-center">${count}</span>`);
const $eleCount2 = $(`<span class="ve-col-2 pr-0 ve-text-center">${count}</span>`);
const listItem = new ListItem(
hash,
@@ -168,7 +168,7 @@ class BestiarySublistManager extends SublistManager {
<div class="lst__wrp-cells best-ecgen__visible--flex lst--border lst__row-inner">
${sublistButtonsMeta.$wrp}
<span class="best-ecgen__name--sub col-3-5">${name}</span>
<span class="best-ecgen__name--sub ve-col-3-5">${name}</span>
${$hovStatblock}
${$hovToken}
${$hovImage}
@@ -430,12 +430,12 @@ class BestiaryPage extends ListPageMultiSource {
click: evt => this._handleBestiaryLinkClick(evt),
children: [
this._encounterBuilder.getButtons(mI),
e_({tag: "span", clazz: `best-ecgen__name bold col-4-2 pl-0`, text: mon.name}),
e_({tag: "span", clazz: `col-4-1`, text: type}),
e_({tag: "span", clazz: `col-1-7 ve-text-center`, text: cr}),
e_({tag: "span", clazz: `best-ecgen__name bold ve-col-4-2 pl-0`, text: mon.name}),
e_({tag: "span", clazz: `ve-col-4-1`, text: type}),
e_({tag: "span", clazz: `ve-col-1-7 ve-text-center`, text: cr}),
e_({
tag: "span",
clazz: `col-2 ve-text-center ${Parser.sourceJsonToColor(mon.source)} pr-0`,
clazz: `ve-col-2 ve-text-center ${Parser.sourceJsonToColor(mon.source)} pr-0`,
style: Parser.sourceJsonToStylePart(mon.source),
title: `${Parser.sourceJsonToFull(mon.source)}${Renderer.utils.getSourceSubText(mon)}`,
text: source,

View File

@@ -295,7 +295,7 @@ export class EncounterBuilderUiBestiary extends EncounterBuilderUi {
getButtons (monId) {
return e_({
tag: "span",
clazz: `best-ecgen__visible col-1 no-wrap pl-0 btn-group`,
clazz: `best-ecgen__visible ve-col-1 no-wrap pl-0 btn-group`,
click: evt => {
evt.preventDefault();
evt.stopPropagation();
@@ -343,7 +343,7 @@ export class EncounterBuilderUiBestiary extends EncounterBuilderUi {
.click(() => this._sublistManager.pSetDataEntry({sublistItem, key: "isLocked", value: !sublistItem.data.isLocked}))
.toggleClass("active", sublistItem.data.isLocked);
const $wrp = $$`<span class="best-ecgen__visible col-1-5 no-wrap pl-0 btn-group">
const $wrp = $$`<span class="best-ecgen__visible ve-col-1-5 no-wrap pl-0 btn-group">
${$btnAdd}
${$btnSub}
${$btnRandomize}

View File

@@ -220,10 +220,10 @@ class BlocklistUi {
});
const $wrpFilterTools = $$`<div class="input-group input-group--bottom ve-flex no-shrink">
<button class="col-4 sort btn btn-default btn-xs ve-grow" data-sort="source">Source</button>
<button class="col-2 sort btn btn-default btn-xs" data-sort="category">Category</button>
<button class="col-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="col-1 sort btn btn-default btn-xs" disabled>&nbsp;</button>
<button class="ve-col-4 sort btn btn-default btn-xs ve-grow" data-sort="source">Source</button>
<button class="ve-col-2 sort btn btn-default btn-xs" data-sort="category">Category</button>
<button class="ve-col-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="ve-col-1 sort btn btn-default btn-xs" disabled>&nbsp;</button>
</div>`;
const $wrpList = $(`<div class="list-display-only smooth-scroll overflow-y-auto h-100 min-h-0"></div>`);
@@ -533,10 +533,10 @@ class BlocklistUi {
});
const $ele = $$`<div class="${this._addListItem_getItemStyles()}">
<span class="col-4 ve-text-center">${sourceFull}</span>
<span class="col-2 ve-text-center">${display.displayCategory}</span>
<span class="col-5 ve-text-center">${displayName}</span>
<span class="col-1 ve-text-center">${$btnRemove}</span>
<span class="ve-col-4 ve-text-center">${sourceFull}</span>
<span class="ve-col-2 ve-text-center">${display.displayCategory}</span>
<span class="ve-col-5 ve-text-center">${displayName}</span>
<span class="ve-col-1 ve-text-center">${$btnRemove}</span>
</div>`;
const listItem = new ListItem(

View File

@@ -14,8 +14,8 @@ class BooksList extends AdventuresBooksList {
},
rowBuilderFn: (bk) => {
return `
<span class="col-1-3 ve-text-center">${AdventuresBooksList._getGroupStr(bk)}</span>
<span class="col-8-5 bold">${bk.name}</span>
<span class="ve-col-1-3 ve-text-center">${AdventuresBooksList._getGroupStr(bk)}</span>
<span class="ve-col-8-5 bold">${bk.name}</span>
<span class="ve-grow ve-text-center code">${AdventuresBooksList._getDateStr(bk)}</span>
`;
},

View File

@@ -5,12 +5,12 @@ class CharCreationOptionsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Type",
css: "col-5 ve-text-center pl-0",
css: "ve-col-5 ve-text-center pl-0",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Name",
css: "bold col-7 pr-0",
css: "bold ve-col-7 pr-0",
colStyle: "",
}),
];
@@ -72,9 +72,9 @@ class CharCreationOptionsPage extends ListPage {
const source = Parser.sourceJsonToAbv(it.source);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-5 ve-text-center pl-0">${it._fOptionType}</span>
<span class="bold col-5">${it.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)}" title="${Parser.sourceJsonToFull(it.source)} pr-0" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-5 ve-text-center pl-0">${it._fOptionType}</span>
<span class="bold ve-col-5">${it.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)}" title="${Parser.sourceJsonToFull(it.source)} pr-0" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -539,8 +539,8 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
const source = Parser.sourceJsonToAbv(cls.source);
const $lnk = $(`<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-8 pl-0">${cls.name}</span>
<span class="col-4 ve-text-center ${Parser.sourceJsonToColor(cls.source)} pr-0" title="${Parser.sourceJsonToFull(cls.source)}" ${Parser.sourceJsonToStyle(cls.source)}>${source}</span>
<span class="bold ve-col-8 pl-0">${cls.name}</span>
<span class="ve-col-4 ve-text-center ${Parser.sourceJsonToColor(cls.source)} pr-0" title="${Parser.sourceJsonToFull(cls.source)}" ${Parser.sourceJsonToStyle(cls.source)}>${source}</span>
</a>`);
const $ele = $$`<li class="lst__row ve-flex-col ${isExcluded ? "row--blocklisted" : ""}">${$lnk}</li>`;

View File

@@ -5,12 +5,12 @@ class ConditionsDiseasesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Type",
css: "col-2 pl-0 ve-text-center",
css: "ve-col-2 pl-0 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Name",
css: "bold col-10 pr-0",
css: "bold ve-col-10 pr-0",
colStyle: "",
}),
];
@@ -73,10 +73,10 @@ class ConditionsDiseasesPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(it);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="col-3 ve-text-center">${PageFilterConditionsDiseases.getDisplayProp(it.__prop)}</span>
<span class="bold col-6-7 px-1">${it.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="ve-col-3 ve-text-center">${PageFilterConditionsDiseases.getDisplayProp(it.__prop)}</span>
<span class="bold ve-col-6-7 px-1">${it.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>
<div class="ve-flex ve-hidden relative lst__wrp-preview">
<div class="vr-0 absolute lst__vr-preview"></div>

View File

@@ -65,8 +65,13 @@ class BaseConverter extends BaseComponent {
this._state.source = val;
}
get page () { return this._state.page; }
set page (val) { this._state.page = val; }
get mode () { return this._state.mode; }
/* -------------------------------------------- */
renderSidebar (parent, $parent) {
const $wrpSidebar = $(`<div class="w-100 ve-flex-col"/>`).appendTo($parent);
const hkShowSidebar = () => $wrpSidebar.toggleClass("hidden", parent.get("converter") !== this._converterId);
@@ -261,6 +266,21 @@ class BaseConverter extends BaseComponent {
ConverterUiUtil.renderSideMenuDivider($wrpSidebar);
}
// endregion
/* -------------------------------------------- */
renderFooterLhs (parent, {$wrpFooterLhs}) {
if (!this._hasPageNumbers) return;
const $dispPage = $(`<div class="ve-muted italic" title="Use &quot;+&quot; and &quot;-&quot; (when the cursor is not in a text input) to increase/decrease."></div>`)
.appendTo($wrpFooterLhs);
this._addHookBase("page", () => {
$dispPage.html(this._state.page != null ? `<b class="mr-1">Page:</b> ${this._state.page}` : "");
})();
parent.addHook("converter", () => $dispPage.toggleClass("ve-hidden", parent.get("converter") !== this._converterId))();
}
}
class CreatureConverter extends BaseConverter {
@@ -1144,11 +1164,10 @@ class ConverterUi extends BaseComponent {
content: `Saved!`,
});
});
const hkConverter = () => {
this._addHookBase("converter", () => {
$btnSaveLocal.toggleClass("hidden", !this.activeConverter.canSaveLocal);
};
this._addHookBase("converter", hkConverter);
hkConverter();
})();
$(`#btn-output-download`).click(() => {
const metaCurr = this._getCurrentEntities();
@@ -1228,7 +1247,21 @@ class ConverterUi extends BaseComponent {
$("#parsestatblock").on("click", () => doConversion(false));
$(`#parsestatblockadd`).on("click", () => doConversion(true));
this.initSideMenu();
$(document.body)
.on("keydown", evt => {
if (EventUtil.isInInput(evt) || !EventUtil.noModifierKeys(evt)) return;
const key = EventUtil.getKeyIgnoreCapsLock(evt);
if (!["+", "-"].includes(key)) return;
evt.stopPropagation();
evt.preventDefault();
this.activeConverter.page += (key === "+" ? 1 : -1);
});
this._initSideMenu();
this._initFooterLhs();
this._pInit_dispErrorsWarnings();
@@ -1296,7 +1329,7 @@ class ConverterUi extends BaseComponent {
}
}
initSideMenu () {
_initSideMenu () {
const $mnu = $(`.sidemenu`);
const $selConverter = ComponentUiUtil.$getSelEnum(
@@ -1349,6 +1382,15 @@ class ConverterUi extends BaseComponent {
hkMode();
}
_initFooterLhs () {
const $wrpFooterLhs = $(`#wrp-footer-lhs`);
Object
.keys(this._converters)
.sort(SortUtil.ascSortLower)
.forEach(k => this._converters[k].renderFooterLhs(this.getPod(), {$wrpFooterLhs}));
}
doCleanAndOutput (obj, append) {
const asCleanString = CleanUtil.getCleanJson(obj, {isFast: false});
if (append) {

View File

@@ -1395,7 +1395,7 @@ class SpellcastingTraitConvert {
const value = this._getParsedSpells({thisLine, isMarkdown});
if (!spellcastingEntry.spells) spellcastingEntry.spells = {"0": {"spells": []}};
spellcastingEntry.spells["0"].spells = value;
} else if (/ [Ll]evel/.test(thisLine) && /(?::| -) /.test(thisLine)) {
} else if (/[- ][Ll]evel/.test(thisLine) && /(?::| -) /.test(thisLine)) {
hasAnyHeader = true;
let property = thisLine.substring(0, 1);
const allSpells = this._getParsedSpells({thisLine, isMarkdown});
@@ -1403,7 +1403,7 @@ class SpellcastingTraitConvert {
const out = {};
if (thisLine.includes(" slot")) {
const mWarlock = /^(\d)..(?: [Ll]evel)?-(\d).. [Ll]evel \((\d) (\d)..[- ][Ll]evel slots?\)/.exec(thisLine);
const mWarlock = /^(\d)..(?:[- ][Ll]evel)?-(\d)..[- ][Ll]evel \((\d) (\d)..[- ][Ll]evel slots?\)/.exec(thisLine);
if (mWarlock) {
out.lower = parseInt(mWarlock[1]);
out.slots = parseInt(mWarlock[3]);

View File

@@ -235,7 +235,7 @@ class BaseParser {
const cleanLine = curLine.trim();
if (/^\d..-\d.. level\s+\(/.test(cleanLine) && !opts.noSpellcastingWarlockSlotLevel) return false;
if (/^\d..-\d..[- ][Ll]evel\s+\(/.test(cleanLine) && !opts.noSpellcastingWarlockSlotLevel) return false;
// Start of a list item
if (/^[•●]/.test(cleanLine)) return false;
@@ -243,7 +243,7 @@ class BaseParser {
// A lowercase word
if (/^[a-z]/.test(cleanLine) && !opts.noLowercase) return true;
// An ordinal (e.g. "3rd"), but not a spell level (e.g. "1st level")
if (/^\d[a-z][a-z]/.test(cleanLine) && !/^\d[a-z][a-z] level/gi.test(cleanLine)) return true;
if (/^\d[a-z][a-z]/.test(cleanLine) && !/^\d[a-z][a-z][- ][Ll]evel/gi.test(cleanLine)) return true;
// A number (e.g. damage; "5 (1d6 + 2)"), optionally with slash-separated parts (e.g. "30/120 ft.")
if (/^\d+(\/\d+)*\s+/.test(cleanLine) && !opts.noNumber) return true;
// Opening brackets (e.g. damage; "(1d6 + 2)")

View File

@@ -88,12 +88,12 @@ function addMonsterFeatures (mfData) {
$wrpMonFeatures.append(`
<label class="row crc__mon_feature ui-tip__parent">
<div class="col-1 crc__mon_feature_wrp_cb">
<div class="ve-col-1 crc__mon_feature_wrp_cb">
<input type="checkbox" id="mf-${Parser.stringToSlug(f.name)}" title="${f.name}" data-hp="${f.hp || ""}" data-ac="${f.ac || ""}" data-dpr="${f.dpr || ""}" data-attackbonus="${f.attackBonus || ""}" class="crc__mon_feature_cb">${numBox}
</div>
<div class="col-2">${f.name}</div>
<div class="col-2">${Renderer.get().render(`{@creature ${f.example}}`)}</div>
<div class="col-7"><span title="${effectOnCr.join(", ")}">${Renderer.get().render(f.effect)}</span></div>
<div class="ve-col-2">${f.name}</div>
<div class="ve-col-2">${Renderer.get().render(`{@creature ${f.example}}`)}</div>
<div class="ve-col-7"><span title="${effectOnCr.join(", ")}">${Renderer.get().render(f.effect)}</span></div>
</label>
`);
});

View File

@@ -5,17 +5,17 @@ class CultsBoonsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Type",
css: "col-2 ve-text-center pl-0",
css: "ve-col-2 ve-text-center pl-0",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Subtype",
css: "col-2 ve-text-center",
css: "ve-col-2 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Name",
css: "bold col-8 pr-0",
css: "bold ve-col-8 pr-0",
colStyle: "",
}),
];
@@ -77,10 +77,10 @@ class CultsBoonsPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(it);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-2 ve-text-center pl-0">${it._lType}</span>
<span class="col-2 ve-text-center">${it._lSubType}</span>
<span class="bold col-6">${it.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-2 ve-text-center pl-0">${it._lType}</span>
<span class="ve-col-2 ve-text-center">${it._lSubType}</span>
<span class="bold ve-col-6">${it.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -5,7 +5,7 @@ class DecksSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-12 pl-0",
css: "bold ve-col-12 pl-0",
colStyle: "",
}),
];
@@ -135,8 +135,8 @@ class DecksPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(ent);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-10 bold pl-0">${ent.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(ent.source)} pr-0" title="${Parser.sourceJsonToFull(ent.source)}" ${Parser.sourceJsonToStyle(ent.source)}>${source}</span>
<span class="ve-col-10 bold pl-0">${ent.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(ent.source)} pr-0" title="${Parser.sourceJsonToFull(ent.source)}" ${Parser.sourceJsonToStyle(ent.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -5,22 +5,22 @@ class DeitiesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-4 pl-0",
css: "bold ve-col-4 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Pantheon",
css: "col-2 ve-text-center",
css: "ve-col-2 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Alignment",
css: "col-2 ve-text-center",
css: "ve-col-2 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Domains",
css: "col-4",
css: "ve-col-4",
colStyle: "",
}),
];
@@ -84,11 +84,11 @@ class DeitiesPage extends ListPage {
const domains = ent.domains.join(", ");
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-3 pl-0">${ent.name}</span>
<span class="col-2 ve-text-center">${ent.pantheon}</span>
<span class="col-2 ve-text-center">${alignment}</span>
<span class="col-3 ${ent.domains[0] === VeCt.STR_NONE ? `list-entry-none` : ""}">${domains}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(ent.source)} pr-0" title="${Parser.sourceJsonToFull(ent.source)}" ${Parser.sourceJsonToStyle(ent.source)}>${source}</span>
<span class="bold ve-col-3 pl-0">${ent.name}</span>
<span class="ve-col-2 ve-text-center">${ent.pantheon}</span>
<span class="ve-col-2 ve-text-center">${alignment}</span>
<span class="ve-col-3 ${ent.domains[0] === VeCt.STR_NONE ? `list-entry-none` : ""}">${domains}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(ent.source)} pr-0" title="${Parser.sourceJsonToFull(ent.source)}" ${Parser.sourceJsonToStyle(ent.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -165,6 +165,14 @@ class Board {
}).appendTo(this.$creen);
}
doToggleFullscreen () {
this.isFullscreen = !this.isFullscreen;
$(document.body).toggleClass("is-fullscreen", this.isFullscreen);
this.doAdjust$creenCss();
this.doSaveStateDebounced();
this.$creen.trigger("panelResize");
}
doHideLoading () {
this.$creen.find(`.dm-screen-loading`).remove();
}
@@ -194,6 +202,14 @@ class Board {
this.initGlobalHandlers();
await this._pLoadTempData();
$(document.body)
.on("keydown", evt => {
if (evt.key !== "Escape" || !this.isFullscreen) return;
evt.stopPropagation();
evt.preventDefault();
this.doToggleFullscreen();
});
window.dispatchEvent(new Event("toolsLoaded"));
}
@@ -765,14 +781,7 @@ class SideMenu {
const $wrpFullscreen = $(`<div class="w-100 ve-flex-vh-center-around"></div>`).appendTo(this.$mnu);
const $btnFullscreen = $(`<button class="btn btn-primary">Toggle Fullscreen</button>`).appendTo($wrpFullscreen);
this.board.$btnFullscreen = $btnFullscreen;
$btnFullscreen.on("click", () => {
this.board.isFullscreen = !this.board.isFullscreen;
if (this.board.isFullscreen) $(`body`).addClass(`is-fullscreen`);
else $(`body`).removeClass(`is-fullscreen`);
this.board.doAdjust$creenCss();
this.board.doSaveStateDebounced();
this.board.$creen.trigger("panelResize");
});
$btnFullscreen.on("click", () => this.board.doToggleFullscreen());
const $btnLockPanels = $(`<button class="btn btn-danger" title="Lock Panels"><span class="glyphicon glyphicon-lock"/></button>`).appendTo($wrpFullscreen);
this.board.$btnLockPanels = $btnLockPanels;
$btnLockPanels.on("click", () => {
@@ -2111,9 +2120,26 @@ class Panel {
const $wrpContent = $(`<div class="panel-content-wrapper"/>`).appendTo($pnl);
const $wrpBtnAdd = $(`<div class="panel-add"/>`).appendTo($wrpContent);
const $btnAdd = $(`<span class="btn-panel-add glyphicon glyphicon-plus"/>`).on("click", () => {
openAddMenu();
}).appendTo($wrpBtnAdd);
const $btnAdd = $(`<span class="btn-panel-add glyphicon glyphicon-plus"/>`)
.on("click", () => {
openAddMenu();
})
.on("drop", async evt => {
evt = evt.originalEvent;
const data = EventUtil.getDropJson(evt);
if (!data) return;
if (data.type !== VeCt.DRAG_TYPE_IMPORT) return;
evt.stopPropagation();
evt.preventDefault();
const {page, source, hash} = data;
// FIXME(Future) "Stats" may not be the correct panel type, but works in most useful cases
this.doPopulate_Stats(page, source, hash);
})
.appendTo($wrpBtnAdd);
this.$btnAdd = $wrpBtnAdd;
this.$btnAddInner = $btnAdd;
this.$pnlWrpContent = $wrpContent;
@@ -3846,10 +3872,10 @@ class UnitConverter {
dirConv = 1;
updateDisplay();
};
$(`<td class="col-3">${u.n1}</td>`).click(clickL).appendTo($tr);
$(`<td class="col-3 code">×${u.x1.padStart(5)}</td>`).click(clickL).appendTo($tr);
$(`<td class="col-3">${u.n2}</td>`).click(clickR).appendTo($tr);
$(`<td class="col-3 code">×${u.x2.padStart(5)}</td>`).click(clickR).appendTo($tr);
$(`<td class="ve-col-3">${u.n1}</td>`).click(clickL).appendTo($tr);
$(`<td class="ve-col-3 code">×${u.x1.padStart(5)}</td>`).click(clickL).appendTo($tr);
$(`<td class="ve-col-3">${u.n2}</td>`).click(clickR).appendTo($tr);
$(`<td class="ve-col-3 code">×${u.x2.padStart(5)}</td>`).click(clickR).appendTo($tr);
});
const $wrpIpt = $(`<div class="split dm-unitconv__wrp-ipt"/>`).appendTo($wrpConverter);

View File

@@ -195,16 +195,16 @@ export class InitiativeTrackerConditionAdd extends BaseComponent {
return $$`
<div class="ve-flex-v-center mb-2">
<div class="small-caps col-5 pr-1">Name</div>
<div class="small-caps col-2 px-1">Color</div>
<div class="small-caps col-4 px-1">Duration</div>
<div class="col-1 pl-1">&nbsp;</div>
<div class="small-caps ve-col-5 pr-1">Name</div>
<div class="small-caps ve-col-2 px-1">Color</div>
<div class="small-caps ve-col-4 px-1">Duration</div>
<div class="ve-col-1 pl-1">&nbsp;</div>
</div>
<div class="ve-flex-v-center mb-3">
<div class="col-5 pr-1">${$iptName}</div>
<div class="col-2 px-1">${$iptColor}</div>
<div class="col-4 px-1">${$iptTurns}</div>
<div class="col-1 pl-1">${$btnSave}</div>
<div class="ve-col-5 pr-1">${$iptName}</div>
<div class="ve-col-2 px-1">${$iptColor}</div>
<div class="ve-col-4 px-1">${$iptTurns}</div>
<div class="ve-col-1 pl-1">${$btnSave}</div>
</div>
`;
}

View File

@@ -26,9 +26,9 @@ class _RenderableCollectionConditionsCustomEdit extends RenderableCollectionGene
$$($wrpRow)`
<div class="ve-flex-vh-center w-100 my-1">
<div class="col-5 pr-1 ve-flex-v-center">${$iptName}</div>
<div class="col-2 px-1 ve-flex-v-center">${$iptColor}</div>
<div class="col-5 pr-1 ve-flex-v-center">
<div class="ve-col-5 pr-1 ve-flex-v-center">${$iptName}</div>
<div class="ve-col-2 px-1 ve-flex-v-center">${$iptColor}</div>
<div class="ve-col-5 pr-1 ve-flex-v-center">
${$iptTurns}
<div class="ve-flex-vh-center btn-group">
${$btnDelete}
@@ -78,10 +78,10 @@ export class InitiativeTrackerConditionCustomEdit extends BaseComponent {
$$($modalInner)`
<div class="ve-flex-col mt-2 h-100 min-h-0">
<div class="ve-flex-vh-center w-100 mb-2 bb-1p-trans">
<div class="col-5">Name</div>
<div class="col-2">Color</div>
<div class="col-4">Turns</div>
<div class="col-1 ve-flex-v-center ve-flex-h-right">${$btnAdd}</div>
<div class="ve-col-5">Name</div>
<div class="ve-col-2">Color</div>
<div class="ve-col-4">Turns</div>
<div class="ve-col-1 ve-flex-v-center ve-flex-h-right">${$btnAdd}</div>
</div>
${$wrpRows}

View File

@@ -193,7 +193,7 @@ export class InitiativeTrackerNetworking {
else fnDispServerStoppedState();
$$`<div class="row w-100">
<div class="col-12">
<div class="ve-col-12">
<p>
The Player View is part of a peer-to-peer system to allow players to connect to a DM's initiative tracker. Players should use the <a href="inittrackerplayerview.html">Initiative Tracker Player View</a> page to connect to the DM's instance. As a DM, the usage is as follows:
<ol>
@@ -327,7 +327,7 @@ export class InitiativeTrackerNetworking {
const $btnAltGenAll = $(`<button class="btn btn-primary btn-text-insert">Generate All</button>`).click(() => $btnGenServerTokens.click());
const $btnAltCopyAll = $(`<button class="btn btn-primary btn-text-insert">Copy Server Tokens</button>`).click(() => $btnCopyServers.click());
$$`<div class="ve-flex w-100">
<div class="col-12">
<div class="ve-col-12">
<p>
The Player View is part of a peer-to-peer (i.e., serverless) system to allow players to connect to a DM's initiative tracker. Players should use the <a href="inittrackerplayerview.html">Initiative Tracker Player View</a> page to connect to the DM's instance. As a DM, the usage is as follows:
<ol>
@@ -401,7 +401,7 @@ export class InitiativeTrackerNetworking {
$$`
<div class="ve-flex w-100">
<div class="col-12">
<div class="ve-col-12">
<div class="ve-flex-inline-v-center mr-2">
<span class="mr-1">Add a player (client):</span>
${$btnAddClient}
@@ -426,10 +426,10 @@ export class InitiativeTrackerNetworking {
UiUtil.$getAddModalRow($modalInner, "div")
.append($$`
<div class="ve-flex w-100">
<div class="col-2 bold">Player Name</div>
<div class="col-3-5 bold">Server Token</div>
<div class="col-1 ve-text-center">${$btnGenServerTokens}</div>
<div class="col-3-5 bold">Client Token</div>
<div class="ve-col-2 bold">Player Name</div>
<div class="ve-col-3-5 bold">Server Token</div>
<div class="ve-col-1 ve-text-center">${$btnGenServerTokens}</div>
<div class="ve-col-3-5 bold">Client Token</div>
</div>
`);
@@ -441,12 +441,12 @@ export class InitiativeTrackerNetworking {
$btnAcceptClientToken,
$btnDeleteClient,
) => $$`<div class="w-100 mb-2 ve-flex">
<div class="col-2 pr-1">${$iptName}</div>
<div class="col-3-5 px-1">${$iptTokenServer}</div>
<div class="col-1 px-1 ve-flex-vh-center">${$btnGenServerToken}</div>
<div class="col-3-5 px-1">${$iptTokenClient}</div>
<div class="col-1-5 px-1 ve-flex-vh-center">${$btnAcceptClientToken}</div>
<div class="col-0-5 pl-1 ve-flex-vh-center">${$btnDeleteClient}</div>
<div class="ve-col-2 pr-1">${$iptName}</div>
<div class="ve-col-3-5 px-1">${$iptTokenServer}</div>
<div class="ve-col-1 px-1 ve-flex-vh-center">${$btnGenServerToken}</div>
<div class="ve-col-3-5 px-1">${$iptTokenClient}</div>
<div class="ve-col-1-5 px-1 ve-flex-vh-center">${$btnAcceptClientToken}</div>
<div class="ve-col-0-5 pl-1 ve-flex-vh-center">${$btnDeleteClient}</div>
</div>`;
const clientRowMetas = [];

View File

@@ -44,13 +44,13 @@ class _RenderableCollectionStatsCols extends RenderableCollectionGenericRows {
const $padDrag = this._utils.$getPadDrag({$wrpRow});
$$($wrpRow)`
<div class="col-5 pr-1">${meta.constructor.NAME}</div>
<div class="col-3 pr-1">${$iptAbv}</div>
<div class="col-1-5 ve-text-center">${$cbIsEditable}</div>
<div class="col-1-5 ve-text-center">${$btnVisible}</div>
<div class="ve-col-5 pr-1">${meta.constructor.NAME}</div>
<div class="ve-col-3 pr-1">${$iptAbv}</div>
<div class="ve-col-1-5 ve-text-center">${$cbIsEditable}</div>
<div class="ve-col-1-5 ve-text-center">${$btnVisible}</div>
<div class="col-0-5 ve-flex-vh-center">${$btnDelete}</div>
<div class="col-0-5 ve-flex-vh-center">${$padDrag}</div>
<div class="ve-col-0-5 ve-flex-vh-center">${$btnDelete}</div>
<div class="ve-col-0-5 ve-flex-vh-center">${$padDrag}</div>
`;
}
}
@@ -179,11 +179,11 @@ export class InitiativeTrackerSettings extends BaseComponent {
.click(evt => ContextUtil.pOpenMenu(evt, menuAddStatsCol));
const $wrpTblStatsHead = $$`<div class="ve-flex-vh-center w-100 mb-2 bb-1p-trans">
<div class="col-5">Contains</div>
<div class="col-3">Abbreviation</div>
<div class="col-1-5 ve-text-center help" title="Only affects creatures. Players are always editable.">Editable</div>
<div class="col-1-5">&nbsp;</div>
<div class="col-1 ve-flex-v-center ve-flex-h-right">${$btnAddRow}</div>
<div class="ve-col-5">Contains</div>
<div class="ve-col-3">Abbreviation</div>
<div class="ve-col-1-5 ve-text-center help" title="Only affects creatures. Players are always editable.">Editable</div>
<div class="ve-col-1-5">&nbsp;</div>
<div class="ve-col-1 ve-flex-v-center ve-flex-h-right">${$btnAddRow}</div>
</div>`
.appendTo($modalInner);

View File

@@ -85,7 +85,7 @@ export class EncounterBuilderUi extends BaseComponent {
<div class="btn-group mr-3">
${$btnRandom}
${$btnRandomMode}
<ul class="dropdown-menu">
<ul class="ve-dropdown-menu">
${$liRandomEasy}
${$liRandomMedium}
${$liRandomHard}
@@ -96,7 +96,7 @@ export class EncounterBuilderUi extends BaseComponent {
<div class="btn-group">
${$btnAdjust}
${$btnAdjustMode}
<ul class="dropdown-menu">
<ul class="ve-dropdown-menu">
${$liAdjustEasy}
${$liAdjustMedium}
${$liAdjustHard}
@@ -142,7 +142,7 @@ export class EncounterBuilderUi extends BaseComponent {
await pSetRandomMode(modeRandom);
});
const $btnRandomMode = $(`<button class="btn btn-primary dropdown-toggle"><span class="caret"></span></button>`);
const $btnRandomMode = $(`<button class="btn btn-primary ve-dropdown-toggle"><span class="caret"></span></button>`);
JqueryUtil.bindDropdownButton($btnRandomMode);
return {
@@ -189,7 +189,7 @@ export class EncounterBuilderUi extends BaseComponent {
await pSetAdjustMode(modeAdjust);
});
const $btnAdjustMode = $(`<button class="btn btn-primary dropdown-toggle"><span class="caret"></span></button>`);
const $btnAdjustMode = $(`<button class="btn btn-primary ve-dropdown-toggle"><span class="caret"></span></button>`);
JqueryUtil.bindDropdownButton($btnAdjustMode);
return {

View File

@@ -5,17 +5,17 @@ class FeatsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-4 pl-0",
css: "bold ve-col-4 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Ability",
css: "col-4",
css: "ve-col-4",
colStyle: "",
}),
new SublistCellTemplate({
name: "Prerequisite",
css: "col-4 pr-0",
css: "ve-col-4 pr-0",
colStyle: "",
}),
];
@@ -84,11 +84,11 @@ class FeatsPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(feat);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="bold col-3-5 px-1">${feat.name}</span>
<span class="col-3-5 ${feat._slAbility === VeCt.STR_NONE ? "list-entry-none " : ""}">${feat._slAbility}</span>
<span class="col-3 ${feat._slPrereq === VeCt.STR_NONE ? "list-entry-none " : ""}">${feat._slPrereq}</span>
<span class="source col-1-7 ve-text-center ${Parser.sourceJsonToColor(feat.source)} pr-0" title="${Parser.sourceJsonToFull(feat.source)}" ${Parser.sourceJsonToStyle(feat.source)}>${source}</span>
<span class="ve-col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="bold ve-col-3-5 px-1">${feat.name}</span>
<span class="ve-col-3-5 ${feat._slAbility === VeCt.STR_NONE ? "list-entry-none " : ""}">${feat._slAbility}</span>
<span class="ve-col-3 ${feat._slPrereq === VeCt.STR_NONE ? "list-entry-none " : ""}">${feat._slPrereq}</span>
<span class="source ve-col-1-7 ve-text-center ${Parser.sourceJsonToColor(feat.source)} pr-0" title="${Parser.sourceJsonToFull(feat.source)}" ${Parser.sourceJsonToStyle(feat.source)}>${source}</span>
</a>
<div class="ve-flex ve-hidden relative lst__wrp-preview">
<div class="vr-0 absolute lst__vr-preview"></div>

View File

@@ -145,15 +145,15 @@ class ModalFilterBackgrounds extends ModalFilter {
const source = Parser.sourceJsonToAbv(bg.source);
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-4 ${bg._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${bg._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${bg.name}</div>
<div class="col-6">${bg._skillDisplay}</div>
<div class="col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(bg.source)}" title="${Parser.sourceJsonToFull(bg.source)}" ${Parser.sourceJsonToStyle(bg.source)}>${source}${Parser.sourceJsonToMarkerHtml(bg.source)}</div>
<div class="ve-col-4 ${bg._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${bg._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${bg.name}</div>
<div class="ve-col-6">${bg._skillDisplay}</div>
<div class="ve-col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(bg.source)}" title="${Parser.sourceJsonToFull(bg.source)}" ${Parser.sourceJsonToStyle(bg.source)}>${source}${Parser.sourceJsonToMarkerHtml(bg.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -670,16 +670,16 @@ class ModalFilterBestiary extends ModalFilter {
const cr = mon._pCr;
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-4 ${mon._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${mon._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${mon.name}</div>
<div class="col-4">${type}</div>
<div class="col-2 ve-text-center">${cr}</div>
<div class="col-1 ve-flex-h-center ${Parser.sourceJsonToColor(mon.source)} pr-0" title="${Parser.sourceJsonToFull(mon.source)}" ${Parser.sourceJsonToStyle(mon.source)}>${source}${Parser.sourceJsonToMarkerHtml(mon.source)}</div>
<div class="ve-col-4 ${mon._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${mon._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${mon.name}</div>
<div class="ve-col-4">${type}</div>
<div class="ve-col-2 ve-text-center">${cr}</div>
<div class="ve-col-1 ve-flex-h-center ${Parser.sourceJsonToColor(mon.source)} pr-0" title="${Parser.sourceJsonToFull(mon.source)}" ${Parser.sourceJsonToStyle(mon.source)}>${source}${Parser.sourceJsonToMarkerHtml(mon.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -757,9 +757,9 @@ class ModalFilterClasses extends ModalFilter {
const $wrpFormBottom = $(`<div class="w-100"></div>`);
const $wrpFormHeaders = $(`<div class="input-group input-group--bottom ve-flex no-shrink">
<div class="btn btn-default disabled col-1 pl-0"></div>
<button class="col-9 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="col-2 pr-0 sort btn btn-default btn-xs ve-grow" data-sort="source">Source</button>
<div class="btn btn-default disabled ve-col-1 pl-0"></div>
<button class="ve-col-9 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="ve-col-2 pr-0 sort btn btn-default btn-xs ve-grow" data-sort="source">Source</button>
</div>`);
const $wrpForm = $$`<div class="ve-flex-col w-100 mb-2">${$wrpFormTop}${$wrpFormBottom}${$wrpFormHeaders}</div>`;
@@ -954,9 +954,9 @@ class ModalFilterClasses extends ModalFilter {
const source = Parser.sourceJsonToAbv(cls.source);
eleLabel.innerHTML = `<div class="col-1 pl-0 ve-flex-vh-center"><div class="fltr-cls__tgl"></div></div>
<div class="bold col-9 ${cls._versionBase_isVersion ? "italic" : ""}">${cls._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${cls.name}</div>
<div class="col-2 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(cls.source)}" title="${Parser.sourceJsonToFull(cls.source)}" ${Parser.sourceJsonToStyle(cls.source)}>${source}${Parser.sourceJsonToMarkerHtml(cls.source)}</div>`;
eleLabel.innerHTML = `<div class="ve-col-1 pl-0 ve-flex-vh-center"><div class="fltr-cls__tgl"></div></div>
<div class="bold ve-col-9 ${cls._versionBase_isVersion ? "italic" : ""}">${cls._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${cls.name}</div>
<div class="ve-col-2 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(cls.source)}" title="${Parser.sourceJsonToFull(cls.source)}" ${Parser.sourceJsonToStyle(cls.source)}>${source}${Parser.sourceJsonToMarkerHtml(cls.source)}</div>`;
return new ListItem(
clsI,
@@ -978,9 +978,9 @@ class ModalFilterClasses extends ModalFilter {
const source = Parser.sourceJsonToAbv(sc.source);
eleLabel.innerHTML = `<div class="col-1 pl-0 ve-flex-vh-center"><div class="fltr-cls__tgl"></div></div>
<div class="col-9 pl-1 ve-flex-v-center ${sc._versionBase_isVersion ? "italic" : ""}">${sc._versionBase_isVersion ? `<span class="px-3"></span>` : ""}<span class="mx-3">\u2014</span> ${sc.name}</div>
<div class="col-2 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(sc.source)}" title="${Parser.sourceJsonToFull(sc.source)}" ${Parser.sourceJsonToStyle(sc.source)}>${source}${Parser.sourceJsonToMarkerHtml(sc.source)}</div>`;
eleLabel.innerHTML = `<div class="ve-col-1 pl-0 ve-flex-vh-center"><div class="fltr-cls__tgl"></div></div>
<div class="ve-col-9 pl-1 ve-flex-v-center ${sc._versionBase_isVersion ? "italic" : ""}">${sc._versionBase_isVersion ? `<span class="px-3"></span>` : ""}<span class="mx-3">\u2014</span> ${sc.name}</div>
<div class="ve-col-2 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(sc.source)}" title="${Parser.sourceJsonToFull(sc.source)}" ${Parser.sourceJsonToStyle(sc.source)}>${source}${Parser.sourceJsonToMarkerHtml(sc.source)}</div>`;
return new ListItem(
`${clsI}--${scI}`,

View File

@@ -190,16 +190,16 @@ class ModalFilterFeats extends ModalFilter {
const source = Parser.sourceJsonToAbv(feat.source);
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-4 ${feat._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${feat._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${feat.name}</div>
<span class="col-3 ${feat._slAbility === VeCt.STR_NONE ? "italic" : ""}">${feat._slAbility}</span>
<span class="col-3 ${feat._slPrereq === VeCt.STR_NONE ? "italic" : ""}">${feat._slPrereq}</span>
<div class="col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(feat.source)}" title="${Parser.sourceJsonToFull(feat.source)}" ${Parser.sourceJsonToStyle(feat.source)}>${source}${Parser.sourceJsonToMarkerHtml(feat.source)}</div>
<div class="ve-col-4 ${feat._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${feat._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${feat.name}</div>
<span class="ve-col-3 ${feat._slAbility === VeCt.STR_NONE ? "italic" : ""}">${feat._slAbility}</span>
<span class="ve-col-3 ${feat._slPrereq === VeCt.STR_NONE ? "italic" : ""}">${feat._slPrereq}</span>
<div class="ve-col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(feat.source)}" title="${Parser.sourceJsonToFull(feat.source)}" ${Parser.sourceJsonToStyle(feat.source)}>${source}${Parser.sourceJsonToMarkerHtml(feat.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -494,15 +494,15 @@ class ModalFilterItems extends ModalFilter {
const type = item._typeListText.join(", ");
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-5 ${item._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${item._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${item.name}</div>
<div class="col-5">${type.uppercaseFirst()}</div>
<div class="col-1 ve-flex-h-center ${Parser.sourceJsonToColor(item.source)} pr-0" title="${Parser.sourceJsonToFull(item.source)}" ${Parser.sourceJsonToStyle(item.source)}>${source}${Parser.sourceJsonToMarkerHtml(item.source)}</div>
<div class="ve-col-5 ${item._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${item._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${item.name}</div>
<div class="ve-col-5">${type.uppercaseFirst()}</div>
<div class="ve-col-1 ve-flex-h-center ${Parser.sourceJsonToColor(item.source)} pr-0" title="${Parser.sourceJsonToFull(item.source)}" ${Parser.sourceJsonToStyle(item.source)}>${source}${Parser.sourceJsonToMarkerHtml(item.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -215,17 +215,17 @@ class ModalFilterOptionalFeatures extends ModalFilter {
const level = Renderer.optionalfeature.getListPrerequisiteLevelText(optfeat.prerequisite);
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-3 ${optfeat._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${optfeat._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${optfeat.name}</div>
<span class="col-2 ve-text-center" title="${optfeat._dFeatureType}">${optfeat._lFeatureType}</span>
<span class="col-4 ve-text-center">${prerequisite}</span>
<span class="col-1 ve-text-center">${level}</span>
<div class="col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(optfeat.source)}" title="${Parser.sourceJsonToFull(optfeat.source)}" ${Parser.sourceJsonToStyle(optfeat.source)}>${source}${Parser.sourceJsonToMarkerHtml(optfeat.source)}</div>
<div class="ve-col-3 ${optfeat._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${optfeat._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${optfeat.name}</div>
<span class="ve-col-2 ve-text-center" title="${optfeat._dFeatureType}">${optfeat._lFeatureType}</span>
<span class="ve-col-4 ve-text-center">${prerequisite}</span>
<span class="ve-col-1 ve-text-center">${level}</span>
<div class="ve-col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(optfeat.source)}" title="${Parser.sourceJsonToFull(optfeat.source)}" ${Parser.sourceJsonToStyle(optfeat.source)}>${source}${Parser.sourceJsonToMarkerHtml(optfeat.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -277,16 +277,16 @@ class ModalFilterRaces extends ModalFilter {
const source = Parser.sourceJsonToAbv(race.source);
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-4 ${race._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${race._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${race.name}</div>
<div class="col-4">${ability.asTextShort}</div>
<div class="col-2 ve-text-center">${size}</div>
<div class="col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(race.source)}" title="${Parser.sourceJsonToFull(race.source)}" ${Parser.sourceJsonToStyle(race.source)}>${source}${Parser.sourceJsonToMarkerHtml(race.source)}</div>
<div class="ve-col-4 ${race._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${race._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${race.name}</div>
<div class="ve-col-4">${ability.asTextShort}</div>
<div class="ve-col-2 ve-text-center">${size}</div>
<div class="ve-col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(race.source)}" title="${Parser.sourceJsonToFull(race.source)}" ${Parser.sourceJsonToStyle(race.source)}>${source}${Parser.sourceJsonToMarkerHtml(race.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -666,19 +666,19 @@ class ModalFilterSpells extends ModalFilter {
const range = Parser.spRangeToFull(spell.range);
eleRow.innerHTML = `<div class="w-100 ve-flex-vh-center lst--border veapp__list-row no-select lst__wrp-cells">
<div class="col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="ve-col-0-5 pl-0 ve-flex-vh-center">${this._isRadio ? `<input type="radio" name="radio" class="no-events">` : `<input type="checkbox" class="no-events">`}</div>
<div class="col-0-5 px-1 ve-flex-vh-center">
<div class="ve-col-0-5 px-1 ve-flex-vh-center">
<div class="ui-list__btn-inline px-2" title="Toggle Preview (SHIFT to Toggle Info Preview)">[+]</div>
</div>
<div class="col-3 ${spell._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${spell._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${spell.name}</div>
<div class="col-1-5 ve-text-center">${levelText}</div>
<div class="col-2 ve-text-center">${time}</div>
<div class="col-1 sp__school-${spell.school} ve-text-center" title="${Parser.spSchoolAndSubschoolsAbvsToFull(spell.school, spell.subschools)}" ${Parser.spSchoolAbvToStyle(spell.school)}>${school}</div>
<div class="col-0-5 ve-text-center" title="Concentration">${concentration}</div>
<div class="col-2 text-right">${range}</div>
<div class="col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(spell.source)}" title="${Parser.sourceJsonToFull(spell.source)}" ${Parser.sourceJsonToStyle(spell.source)}>${source}${Parser.sourceJsonToMarkerHtml(spell.source)}</div>
<div class="ve-col-3 ${spell._versionBase_isVersion ? "italic" : ""} ${this._getNameStyle()}">${spell._versionBase_isVersion ? `<span class="px-3"></span>` : ""}${spell.name}</div>
<div class="ve-col-1-5 ve-text-center">${levelText}</div>
<div class="ve-col-2 ve-text-center">${time}</div>
<div class="ve-col-1 sp__school-${spell.school} ve-text-center" title="${Parser.spSchoolAndSubschoolsAbvsToFull(spell.school, spell.subschools)}" ${Parser.spSchoolAbvToStyle(spell.school)}>${school}</div>
<div class="ve-col-0-5 ve-text-center" title="Concentration">${concentration}</div>
<div class="ve-col-2 text-right">${range}</div>
<div class="ve-col-1 pr-0 ve-flex-h-center ${Parser.sourceJsonToColor(spell.source)}" title="${Parser.sourceJsonToFull(spell.source)}" ${Parser.sourceJsonToStyle(spell.source)}>${source}${Parser.sourceJsonToMarkerHtml(spell.source)}</div>
</div>`;
const btnShowHidePreview = eleRow.firstElementChild.children[1].firstElementChild;

View File

@@ -106,7 +106,7 @@ globalThis.PageFilter = PageFilter;
class ModalFilter {
static _$getFilterColumnHeaders (btnMeta) {
return btnMeta.map((it, i) => $(`<button class="col-${it.width} ${i === 0 ? "pl-0" : i === btnMeta.length ? "pr-0" : ""} ${it.disabled ? "" : "sort"} btn btn-default btn-xs" ${it.disabled ? "" : `data-sort="${it.sort}"`} ${it.title ? `title="${it.title}"` : ""} ${it.disabled ? "disabled" : ""}>${it.text}</button>`));
return btnMeta.map((it, i) => $(`<button class="ve-col-${it.width} ${i === 0 ? "pl-0" : i === btnMeta.length ? "pr-0" : ""} ${it.disabled ? "" : "sort"} btn btn-default btn-xs" ${it.disabled ? "" : `data-sort="${it.sort}"`} ${it.title ? `title="${it.title}"` : ""} ${it.disabled ? "disabled" : ""}>${it.text}</button>`));
}
/**
@@ -136,7 +136,7 @@ class ModalFilter {
_$getWrpList () { return $(`<div class="list ui-list__wrp overflow-x-hidden overflow-y-auto h-100 min-h-0"></div>`); }
_$getColumnHeaderPreviewAll (opts) {
return $(`<button class="btn btn-default btn-xs ${opts.isBuildUi ? "col-1" : "col-0-5"}">${ListUiUtil.HTML_GLYPHICON_EXPAND}</button>`);
return $(`<button class="btn btn-default btn-xs ${opts.isBuildUi ? "ve-col-1" : "ve-col-0-5"}">${ListUiUtil.HTML_GLYPHICON_EXPAND}</button>`);
}
/**
@@ -172,11 +172,11 @@ class ModalFilter {
const $wrpFormHeaders = $(`<div class="input-group input-group--bottom ve-flex no-shrink"></div>`);
const $cbSelAll = opts.isBuildUi || this._isRadio ? null : $(`<input type="checkbox">`);
const $btnSendAllToRight = opts.isBuildUi ? $(`<button class="btn btn-xxs btn-default col-1" title="Add All"><span class="glyphicon glyphicon-arrow-right"></span></button>`) : null;
const $btnSendAllToRight = opts.isBuildUi ? $(`<button class="btn btn-xxs btn-default ve-col-1" title="Add All"><span class="glyphicon glyphicon-arrow-right"></span></button>`) : null;
if (!opts.isBuildUi) {
if (this._isRadio) $wrpFormHeaders.append(`<label class="btn btn-default btn-xs col-0-5 ve-flex-vh-center" disabled></label>`);
else $$`<label class="btn btn-default btn-xs col-0-5 ve-flex-vh-center">${$cbSelAll}</label>`.appendTo($wrpFormHeaders);
if (this._isRadio) $wrpFormHeaders.append(`<label class="btn btn-default btn-xs ve-col-0-5 ve-flex-vh-center" disabled></label>`);
else $$`<label class="btn btn-default btn-xs ve-col-0-5 ve-flex-vh-center">${$cbSelAll}</label>`.appendTo($wrpFormHeaders);
}
const $btnTogglePreviewAll = this._$getColumnHeaderPreviewAll(opts)
@@ -2190,15 +2190,15 @@ class Filter extends FilterBase {
}
_doRenderPills_doRenderWrpGroup_getDivider (group) {
const eleHeader = this._doRenderPills_doRenderWrpGroup_getDividerHeader(group);
const eleHr = this._doRenderPills_doRenderWrpGroup_getDividerHr(group);
const elesHeader = this._doRenderPills_doRenderWrpGroup_getDividerHeaders(group);
return e_({
tag: "div",
clazz: "ve-flex-col w-100",
children: [
eleHr,
eleHeader,
...elesHeader,
]
.filter(Boolean),
});
@@ -2206,15 +2206,17 @@ class Filter extends FilterBase {
_doRenderPills_doRenderWrpGroup_getDividerHr (group) { return e_({tag: "hr", clazz: `fltr__dropdown-divider--sub hr-2 mx-3`}); }
_doRenderPills_doRenderWrpGroup_getDividerHeader (group) {
_doRenderPills_doRenderWrpGroup_getDividerHeaders (group) {
const groupName = this._groupNameFn?.(group);
if (!groupName) return null;
if (!groupName) return [];
return e_({
tag: "div",
clazz: `fltr__divider-header ve-muted italic ve-small`,
text: groupName,
});
return [
e_({
tag: "div",
clazz: `fltr__divider-header ve-muted italic ve-small`,
text: groupName,
}),
];
}
_doRenderPills_doRenderWrpGroup_getWrpPillsSub () { return e_({tag: "div", clazz: `fltr__wrp-pills--sub fltr__container-pills`}); }
@@ -3104,15 +3106,15 @@ class SourceFilter extends Filter {
return [ent.source].concat(otherSourcesFilt.map(src => new SourceFilterItem({item: src.source, isIgnoreRed: true, isOtherSource: true})));
}
_doRenderPills_doRenderWrpGroup_getDividerHeader (group) {
_doRenderPills_doRenderWrpGroup_getDividerHeaders (group) {
switch (group) {
case SourceUtil.FILTER_GROUP_NON_STANDARD: return this._doRenderPills_doRenderWrpGroup_getDividerHeader_groupNonStandard(group);
case SourceUtil.FILTER_GROUP_HOMEBREW: return this._doRenderPills_doRenderWrpGroup_getDividerHeader_groupBrew(group);
default: return super._doRenderPills_doRenderWrpGroup_getDividerHeader(group);
case SourceUtil.FILTER_GROUP_NON_STANDARD: return this._doRenderPills_doRenderWrpGroup_getDividerHeaders_groupNonStandard(group);
case SourceUtil.FILTER_GROUP_HOMEBREW: return this._doRenderPills_doRenderWrpGroup_getDividerHeaders_groupBrew(group);
default: return super._doRenderPills_doRenderWrpGroup_getDividerHeaders(group);
}
}
_doRenderPills_doRenderWrpGroup_getDividerHeader_groupNonStandard (group) {
_doRenderPills_doRenderWrpGroup_getDividerHeaders_groupNonStandard (group) {
let dates = [];
const comp = BaseComponent.fromObject({
min: 0,
@@ -3234,25 +3236,31 @@ class SourceFilter extends Filter {
],
});
return e_({
tag: "div",
clazz: `split-v-center w-100`,
children: [
super._doRenderPills_doRenderWrpGroup_getDividerHeader(group) || e_({clazz: "div"}),
e_({
tag: "div",
clazz: `mb-1 ve-flex-h-right`,
children: [
grpBtnsActive,
grpBtnsInactive,
],
}),
wrpWrpSlider,
],
});
const elesDividerHeaders = super._doRenderPills_doRenderWrpGroup_getDividerHeaders(group);
if (!elesDividerHeaders.length) elesDividerHeaders.push(e_({clazz: "div"}));
if (elesDividerHeaders.length > 1) throw new Error("Unimplemented!");
return [
e_({
tag: "div",
clazz: `split-v-center w-100`,
children: [
...elesDividerHeaders,
e_({
tag: "div",
clazz: `mb-1 ve-flex-h-right`,
children: [
grpBtnsActive,
grpBtnsInactive,
],
}),
],
}),
wrpWrpSlider,
];
}
_doRenderPills_doRenderWrpGroup_getDividerHeader_groupBrew (group) {
_doRenderPills_doRenderWrpGroup_getDividerHeaders_groupBrew (group) {
const btnClear = e_({
tag: "button",
clazz: `btn btn-xxs btn-default px-1`,
@@ -3266,26 +3274,32 @@ class SourceFilter extends Filter {
},
});
return e_({
tag: "div",
clazz: `split-v-center w-100`,
children: [
super._doRenderPills_doRenderWrpGroup_getDividerHeader(group) || e_({clazz: "div"}),
e_({
tag: "div",
clazz: `mb-1 ve-flex-h-right`,
children: [
e_({
tag: "div",
clazz: `ve-flex-v-center btn-group`,
children: [
btnClear,
],
}),
],
}),
],
});
const elesDividerHeaders = super._doRenderPills_doRenderWrpGroup_getDividerHeaders(group);
if (!elesDividerHeaders.length) elesDividerHeaders.push(e_({clazz: "div"}));
if (elesDividerHeaders.length > 1) throw new Error("Unimplemented!");
return [
e_({
tag: "div",
clazz: `split-v-center w-100`,
children: [
...elesDividerHeaders,
e_({
tag: "div",
clazz: `mb-1 ve-flex-h-right`,
children: [
e_({
tag: "div",
clazz: `ve-flex-v-center btn-group`,
children: [
btnClear,
],
}),
],
}),
],
}),
];
}
_toDisplay_getMappedEntryVal (entryVal) {

View File

@@ -130,7 +130,7 @@ class InitTrackerPlayerViewV1 {
$$(tabMeta.$wrpTab)`<div class="ve-flex-col initp__content px-2 py-3 min-h-0">
<div class="initp__initial row">
<div class="col-12">
<div class="ve-col-12">
<p>
The Player View is part of a peer-to-peer (i.e., serverless) system to allow players to connect to a DM's <a href="dmscreen.html">DM Screen</a> initiative tracker. As a player, the usage is as follows:
<ol>
@@ -145,14 +145,14 @@ class InitTrackerPlayerViewV1 {
<hr class="initp__initial">
<div class="initp__initial row w-100 ve-flex">
<div class="col-5 bold mr-4">Player Name</div>
<div class="col-5 bold">Server Token</div>
<div class="col-2 ve-text-center"></div>
<div class="ve-col-5 bold mr-4">Player Name</div>
<div class="ve-col-5 bold">Server Token</div>
<div class="ve-col-2 ve-text-center"></div>
</div>
<div class="initp__initial row w-100 ve-flex mb-4">
<div class="col-5 bold mr-4">${$iptPlayerName}</div>
<div class="col-5 bold">${$iptServerToken}</div>
<div class="col-2 ve-flex-vh-center">${$btnConnect}</div>
<div class="ve-col-5 bold mr-4">${$iptPlayerName}</div>
<div class="ve-col-5 bold">${$iptServerToken}</div>
<div class="ve-col-2 ve-flex-vh-center">${$btnConnect}</div>
</div>
<div class="initp__wrp_active">
@@ -235,7 +235,7 @@ class InitTrackerPlayerViewV0 {
${$dispWarning}
<div class="initp__initial ve-flex">
<div class="col-12">
<div class="ve-col-12">
<p>
The Player View is part of a peer-to-peer (i.e., serverless) system to allow players to connect to a DM's DM Screen initiative tracker. As a player, the usage is as follows:
<ol>
@@ -249,14 +249,14 @@ class InitTrackerPlayerViewV0 {
<hr class="initp__initial">
<div class="initp__initial ve-flex-h-center w-100">
<div class="col-5 bold">Server Token</div>
<div class="col-2 ve-text-center"></div>
<div class="col-5 bold">Client Token</div>
<div class="ve-col-5 bold">Server Token</div>
<div class="ve-col-2 ve-text-center"></div>
<div class="ve-col-5 bold">Client Token</div>
</div>
<div class="initp__initial ve-flex-h-center w-100 flex mb-4">
<div class="col-5 bold">${$iptServerToken}</div>
<div class="col-2 ve-flex-vh-center">${$btnGenClientToken}</div>
<div class="col-5 bold">${$iptClientToken}</div>
<div class="ve-col-5 bold">${$iptServerToken}</div>
<div class="ve-col-2 ve-flex-vh-center">${$btnGenClientToken}</div>
<div class="ve-col-5 bold">${$iptClientToken}</div>
</div>
<div class="initp__wrp_active">

View File

@@ -30,22 +30,22 @@ class ItemsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-6 pl-0",
css: "bold ve-col-6 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Weight",
css: "ve-text-center col-2",
css: "ve-text-center ve-col-2",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Cost",
css: "ve-text-center col-2",
css: "ve-text-center ve-col-2",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Number",
css: "ve-text-center col-2 pr-0",
css: "ve-text-center ve-col-2 pr-0",
colStyle: "text-center",
}),
];
@@ -58,7 +58,7 @@ class ItemsSublistManager extends SublistManager {
item.value || item.valueMult ? Parser.itemValueToFullMultiCurrency(item, {isShortForm: true}).replace(/ +/g, "\u00A0") : "\u2014",
];
const $dispCount = $(`<span class="ve-text-center col-2 pr-0">${count}</span>`);
const $dispCount = $(`<span class="ve-text-center ve-col-2 pr-0">${count}</span>`);
const $ele = $$`<div class="lst__row lst__row--sublist ve-flex-col">
<a href="#${hash}" class="lst--border lst__row-inner">
${this.constructor._getRowCellsHtml({values: cellsText, templates: this.constructor._ROW_TEMPLATE.slice(0, 3)})}
@@ -257,13 +257,13 @@ class ItemsPage extends ListPage {
href: `#${hash}`,
clazz: "lst--border lst__row-inner",
children: [
e_({tag: "span", clazz: `col-3-5 pl-0 bold`, text: item.name}),
e_({tag: "span", clazz: `col-4-5`, text: type}),
e_({tag: "span", clazz: `col-1-5 ve-text-center`, text: `${item.value || item.valueMult ? Parser.itemValueToFullMultiCurrency(item, {isShortForm: true}).replace(/ +/g, "\u00A0") : "\u2014"}`}),
e_({tag: "span", clazz: `col-1-5 ve-text-center`, text: Parser.itemWeightToFull(item, true) || "\u2014"}),
e_({tag: "span", clazz: `ve-col-3-5 pl-0 bold`, text: item.name}),
e_({tag: "span", clazz: `ve-col-4-5`, text: type}),
e_({tag: "span", clazz: `ve-col-1-5 ve-text-center`, text: `${item.value || item.valueMult ? Parser.itemValueToFullMultiCurrency(item, {isShortForm: true}).replace(/ +/g, "\u00A0") : "\u2014"}`}),
e_({tag: "span", clazz: `ve-col-1-5 ve-text-center`, text: Parser.itemWeightToFull(item, true) || "\u2014"}),
e_({
tag: "span",
clazz: `col-1 ve-text-center ${Parser.sourceJsonToColor(item.source)} pr-0`,
clazz: `ve-col-1 ve-text-center ${Parser.sourceJsonToColor(item.source)} pr-0`,
style: Parser.sourceJsonToStylePart(item.source),
title: `${Parser.sourceJsonToFull(item.source)}${Renderer.utils.getSourceSubText(item)}`,
text: source,
@@ -302,14 +302,14 @@ class ItemsPage extends ListPage {
href: `#${hash}`,
clazz: "lst--border lst__row-inner",
children: [
e_({tag: "span", clazz: `col-3-5 pl-0 bold`, text: item.name}),
e_({tag: "span", clazz: `col-4`, text: type}),
e_({tag: "span", clazz: `col-1-5 ve-text-center`, text: Parser.itemWeightToFull(item, true) || "\u2014"}),
e_({tag: "span", clazz: `col-0-6 ve-text-center`, text: item._attunementCategory !== VeCt.STR_NO_ATTUNEMENT ? "×" : ""}),
e_({tag: "span", clazz: `col-1-4 ve-text-center`, text: (item.rarity || "").toTitleCase()}),
e_({tag: "span", clazz: `ve-col-3-5 pl-0 bold`, text: item.name}),
e_({tag: "span", clazz: `ve-col-4`, text: type}),
e_({tag: "span", clazz: `ve-col-1-5 ve-text-center`, text: Parser.itemWeightToFull(item, true) || "\u2014"}),
e_({tag: "span", clazz: `ve-col-0-6 ve-text-center`, text: item._attunementCategory !== VeCt.STR_NO_ATTUNEMENT ? "×" : ""}),
e_({tag: "span", clazz: `ve-col-1-4 ve-text-center`, text: (item.rarity || "").toTitleCase()}),
e_({
tag: "span",
clazz: `col-1 ve-text-center ${Parser.sourceJsonToColor(item.source)} pr-0`,
clazz: `ve-col-1 ve-text-center ${Parser.sourceJsonToColor(item.source)} pr-0`,
style: Parser.sourceJsonToStylePart(item.source),
title: `${Parser.sourceJsonToFull(item.source)}${Renderer.utils.getSourceSubText(item)}`,
text: source,

View File

@@ -11,17 +11,17 @@ class LanguagesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-8 pl-0",
css: "bold ve-col-8 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Type",
css: "col-2 ve-text-center",
css: "ve-col-2 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Script",
css: "col-2 ve-text-center pr-0",
css: "ve-col-2 ve-text-center pr-0",
colStyle: "text-center",
}),
];
@@ -88,10 +88,10 @@ class LanguagesPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(it);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-6 bold pl-0">${it.name}</span>
<span class="col-2 ve-text-center">${(it.type || "\u2014").uppercaseFirst()}</span>
<span class="col-2 ve-text-center">${(it.script || "\u2014").toTitleCase()}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-6 bold pl-0">${it.name}</span>
<span class="ve-col-2 ve-text-center">${(it.type || "\u2014").uppercaseFirst()}</span>
<span class="ve-col-2 ve-text-center">${(it.script || "\u2014").toTitleCase()}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -1359,7 +1359,7 @@ class ListPage {
$btnReset.before($btnHideSearch);
const $btnShowSearch = $(`<button class="btn btn-block btn-default btn-xs" type="button">Show List</button>`);
const $wrpBtnShowSearch = $$`<div class="col-12 mb-1 ve-hidden">${$btnShowSearch}</div>`.prependTo($wrpContent);
const $wrpBtnShowSearch = $$`<div class="ve-col-12 mb-1 ve-hidden">${$btnShowSearch}</div>`.prependTo($wrpContent);
$btnHideSearch.click(() => {
$wrpList.hideVe();

View File

@@ -242,7 +242,7 @@ class LootGenUi extends BaseComponent {
"Item",
],
colStyles: [
"col-2 ve-text-center",
"col-2 text-center",
"col-10",
],
rows: items.map((it, i) => ([i + 1, `{@item ${it.name}|${it.source}}`])),

View File

@@ -63,9 +63,9 @@ class MakeCards extends BaseComponent {
$$($wrpConfig)`<h5 class="split-v-center"><div>New Card Defaults</div>${$btnResetDefaults}</h5>
<div class="ve-flex-v-center bold">
<div class="col-4 ve-text-center pr-2">Type</div>
<div class="col-4 ve-text-center p-2">Color</div>
<div class="col-4 ve-text-center pl-2">Icon</div>
<div class="ve-col-4 ve-text-center pr-2">Type</div>
<div class="ve-col-4 ve-text-center p-2">Color</div>
<div class="ve-col-4 ve-text-center pl-2">Icon</div>
</div>`;
const $getColorIconConfigRow = (entityType) => {
@@ -85,9 +85,9 @@ class MakeCards extends BaseComponent {
hkIcon();
return $$`<div class="ve-flex-v-center stripe-even m-1">
<div class="col-4 ve-flex-vh-center pr-2">${entityMeta.searchTitle}</div>
<div class="col-4 ve-flex-vh-center p-2">${$iptColor}</div>
<div class="col-4 ve-flex-vh-center pl-2">${$btnChooseIcon}</div>
<div class="ve-col-4 ve-flex-vh-center pr-2">${entityMeta.searchTitle}</div>
<div class="ve-col-4 ve-flex-vh-center p-2">${$iptColor}</div>
<div class="ve-col-4 ve-flex-vh-center pl-2">${$btnChooseIcon}</div>
</div>`;
};
@@ -183,14 +183,14 @@ class MakeCards extends BaseComponent {
this._list.visibleItems.forEach(it => it.data.$cbSel.prop("checked", isSel));
});
$$`<div class="w-100 no-shrink ve-flex-v-center bold">
<div class="col-1 mr-2 ve-flex-vh-center">${$cbSelAll}</div>
<div class="col-3 mr-2 ve-flex-vh-center">Name</div>
<div class="col-1-5 mr-2 ve-flex-vh-center">Source</div>
<div class="col-1-5 mr-2 ve-flex-vh-center">Type</div>
<div class="col-1-1 mr-2 ve-flex-vh-center">Color</div>
<div class="col-1-1 mr-2 ve-flex-vh-center">Icon</div>
<div class="col-1 mr-2 ve-flex-vh-center">Count</div>
<div class="col-1-1 ve-flex-v-center ve-flex-h-right"/>
<div class="ve-col-1 mr-2 ve-flex-vh-center">${$cbSelAll}</div>
<div class="ve-col-3 mr-2 ve-flex-vh-center">Name</div>
<div class="ve-col-1-5 mr-2 ve-flex-vh-center">Source</div>
<div class="ve-col-1-5 mr-2 ve-flex-vh-center">Type</div>
<div class="ve-col-1-1 mr-2 ve-flex-vh-center">Color</div>
<div class="ve-col-1-1 mr-2 ve-flex-vh-center">Icon</div>
<div class="ve-col-1 mr-2 ve-flex-vh-center">Count</div>
<div class="ve-col-1-1 ve-flex-v-center ve-flex-h-right"/>
</div>`.appendTo($wrpContainer);
const $wrpList = $(`<div class="w-100 h-100"/>`);
@@ -379,14 +379,14 @@ class MakeCards extends BaseComponent {
});
const $ele = $$`<label class="ve-flex-v-center my-1 w-100 lst__row lst--border lst__row-inner">
<div class="col-1 mr-2 ve-flex-vh-center">${$cbSel}</div>
<div class="col-3 mr-2 ve-flex-v-center">${loaded.name}</div>
<div class="col-1-5 mr-2 ve-flex-vh-center ${Parser.sourceJsonToColor(loaded.source)}" title="${Parser.sourceJsonToFull(loaded.source)}" ${Parser.sourceJsonToStyle(loaded.source)}>${Parser.sourceJsonToAbv(loaded.source)}</div>
<div class="col-1-5 mr-2 ve-flex-vh-center">${Parser.getPropDisplayName(cardMeta.entityType)}</div>
<div class="col-1-1 mr-2 ve-flex-vh-center">${$iptRgb}</div>
<div class="col-1-1 mr-2 ve-flex-vh-center">${$btnIcon}</div>
<div class="col-1 mr-2 ve-flex-vh-center">${$iptCount}</div>
<div class="col-1-1 ve-flex-v-center ve-flex-h-right">${$btnCopy}${$btnDelete}</div>
<div class="ve-col-1 mr-2 ve-flex-vh-center">${$cbSel}</div>
<div class="ve-col-3 mr-2 ve-flex-v-center">${loaded.name}</div>
<div class="ve-col-1-5 mr-2 ve-flex-vh-center ${Parser.sourceJsonToColor(loaded.source)}" title="${Parser.sourceJsonToFull(loaded.source)}" ${Parser.sourceJsonToStyle(loaded.source)}>${Parser.sourceJsonToAbv(loaded.source)}</div>
<div class="ve-col-1-5 mr-2 ve-flex-vh-center">${Parser.getPropDisplayName(cardMeta.entityType)}</div>
<div class="ve-col-1-1 mr-2 ve-flex-vh-center">${$iptRgb}</div>
<div class="ve-col-1-1 mr-2 ve-flex-vh-center">${$btnIcon}</div>
<div class="ve-col-1 mr-2 ve-flex-vh-center">${$iptCount}</div>
<div class="ve-col-1-1 ve-flex-v-center ve-flex-h-right">${$btnCopy}${$btnDelete}</div>
</label>`;
const listItem = new ListItem(
@@ -619,7 +619,7 @@ class MakeCards extends BaseComponent {
.keydown(async evt => {
// prevent double-binding the return key if we have autocomplete enabled
await MiscUtil.pDelay(17); // arbitrary delay to allow dropdown to render (~1000/60, i.e. 1 60 FPS frame)
if ($modalInner.find(`.typeahead.dropdown-menu`).is(":visible")) return;
if ($modalInner.find(`.typeahead.ve-dropdown-menu`).is(":visible")) return;
// return key
if (evt.which === 13) doClose(true);
evt.stopPropagation();

View File

@@ -506,7 +506,7 @@ class NavBar {
}
const a = document.createElement("a");
a.className = "dropdown-toggle";
a.className = "ve-dropdown-toggle";
a.href = "#";
a.setAttribute("role", "button");
a.onclick = function (event) { NavBar._handleDropdownClick(this, event, isSide); };
@@ -517,7 +517,7 @@ class NavBar {
a.innerHTML = `${category} <span class="caret ${isSide ? "caret--right" : ""}"></span>`;
const ul = document.createElement("ul");
ul.className = `dropdown-menu ${isSide ? "dropdown-menu--side" : "dropdown-menu--top"}`;
ul.className = `ve-dropdown-menu ${isSide ? "ve-dropdown-menu--side" : "ve-dropdown-menu--top"}`;
ul.onclick = function (event) { event.stopPropagation(); };
li.appendChild(a);
@@ -644,7 +644,7 @@ class NavBar {
* @private
*/
static _openDropdown_mutAlignment ({liNavbar}) {
const uls = [...liNavbar.querySelectorAll("ul.dropdown-menu")];
const uls = [...liNavbar.querySelectorAll("ul.ve-dropdown-menu")];
const widthRequired = window.innerWidth < 1200
? Math.max(...uls.map(ul => ul.getBoundingClientRect().width))
: uls.map(ul => ul.getBoundingClientRect().width).reduce((a, b) => a + b, 0);

View File

@@ -11,12 +11,12 @@ class ObjectsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-9 pl-0",
css: "bold ve-col-9 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Size",
css: "col-3 pr-0 ve-text-center",
css: "ve-col-3 pr-0 ve-text-center",
colStyle: "text-center",
}),
];
@@ -87,9 +87,9 @@ class ObjectsPage extends ListPage {
const size = Renderer.utils.getRenderedSize(obj.size);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-8 pl-0">${obj.name}</span>
<span class="col-2 ve-text-center">${size}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(obj.source)} pr-0" title="${Parser.sourceJsonToFull(obj.source)}" ${Parser.sourceJsonToStyle(obj.source)}>${source}</span>
<span class="bold ve-col-8 pl-0">${obj.name}</span>
<span class="ve-col-2 ve-text-center">${size}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(obj.source)} pr-0" title="${Parser.sourceJsonToFull(obj.source)}" ${Parser.sourceJsonToStyle(obj.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -1298,7 +1298,7 @@ class IndexableSpecial {
class IndexableSpecialPages extends IndexableSpecial {
pGetIndex () {
return Object.entries(UrlUtil.PG_TO_NAME)
.filter(([page]) => ![UrlUtil.PG_CLASS_SUBCLASS_FEATURES].includes(page))
.filter(([page]) => !UrlUtil.FAUX_PAGES[page])
.map(([page, name]) => ({
n: name,
c: Parser.CAT_ID_PAGE,

View File

@@ -44,7 +44,7 @@ class Omnisearch {
}
this._clickFirst = true;
this._handleClickSubmit(evt);
this._pHandleClickSubmit(evt).then(null);
break;
case "ArrowUp":
evt.preventDefault();
@@ -64,23 +64,23 @@ class Omnisearch {
this._clickFirst = false;
if (evt.which >= 37 && evt.which <= 40) return;
clearTimeout(typeTimer);
typeTimer = setTimeout(() => this._handleClickSubmit(), this._TYPE_TIMEOUT_MS);
typeTimer = setTimeout(() => this._pHandleClickSubmit(), this._TYPE_TIMEOUT_MS);
});
this._iptSearch.onKeydown(() => clearTimeout(typeTimer));
this._iptSearch.onClick(evt => {
evt.stopPropagation();
Renderer.hover.cleanTempWindows();
if (this._iptSearch.val() && this._iptSearch.val().trim().length) this._handleClickSubmit();
if (this._iptSearch.val() && this._iptSearch.val().trim().length) this._pHandleClickSubmit().then(null);
});
this._init_scrollHandler();
this._init_bindBodyListeners();
}
static _handleClickSubmit (evt) {
static async _pHandleClickSubmit (evt) {
if (evt) evt.stopPropagation();
await this._pDoSearch();
Renderer.hover.cleanTempWindows();
return this._pDoSearch();
}
static _init_elements () {
@@ -110,7 +110,7 @@ class Omnisearch {
clazz: "btn btn-default omni__submit",
tabindex: -1,
html: `<span class="glyphicon glyphicon-search"></span>`,
click: evt => this._handleClickSubmit(evt),
click: evt => this._pHandleClickSubmit(evt),
});
this._wrpSearchInput = e_({
@@ -317,7 +317,19 @@ class Omnisearch {
}
static _renderLink_getHoverString (category, url, src, {isFauxPage = false} = {}) {
return `onmouseover="Renderer.hover.pHandleLinkMouseOver(event, this)" onmouseleave="Renderer.hover.handleLinkMouseLeave(event, this)" onmousemove="Renderer.hover.handleLinkMouseMove(event, this)" ondragstart="Renderer.hover.handleLinkDragStart(event, this)" data-vet-page="${UrlUtil.categoryToHoverPage(category).qq()}" data-vet-source="${src.qq()}" data-vet-hash="${url.qq()}" ${isFauxPage ? `data-vet-is-faux-page="true"` : ""} ${Renderer.hover.getPreventTouchString()}`;
return [
`onmouseover="Renderer.hover.pHandleLinkMouseOver(event, this)"`,
`onmouseleave="Renderer.hover.handleLinkMouseLeave(event, this)"`,
`onmousemove="Renderer.hover.handleLinkMouseMove(event, this)"`,
`ondragstart="Renderer.hover.handleLinkDragStart(event, this)"`,
`data-vet-page="${UrlUtil.categoryToHoverPage(category).qq()}"`,
`data-vet-source="${src.qq()}"`,
`data-vet-hash="${url.qq()}"`,
isFauxPage ? `data-vet-is-faux-page="true"` : "",
Renderer.hover.getPreventTouchString(),
]
.filter(Boolean)
.join(" ");
}
static $getResultLink (r) {

View File

@@ -14,22 +14,22 @@ class OptionalFeaturesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-4 pl-0",
css: "bold ve-col-4 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Type",
css: "col-2 ve-text-center",
css: "ve-col-2 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Prerequisite",
css: "col-4-5",
css: "ve-col-4-5",
colStyle: "",
}),
new SublistCellTemplate({
name: "Level",
css: "col-1-5 ve-text-center pr-0",
css: "ve-col-1-5 ve-text-center pr-0",
colStyle: "text-center",
}),
];
@@ -114,12 +114,12 @@ class OptionalFeaturesPage extends ListPage {
const level = Renderer.optionalfeature.getListPrerequisiteLevelText(it.prerequisite);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="bold col-3 px-1">${it.name}</span>
<span class="col-1-5 ve-text-center" title="${it._dFeatureType}">${it._lFeatureType}</span>
<span class="col-4-7">${prerequisite}</span>
<span class="col-1 ve-text-center">${level}</span>
<span class="col-1-5 ${Parser.sourceJsonToColor(it.source)} ve-text-center pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="bold ve-col-3 px-1">${it.name}</span>
<span class="ve-col-1-5 ve-text-center" title="${it._dFeatureType}">${it._lFeatureType}</span>
<span class="ve-col-4-7">${prerequisite}</span>
<span class="ve-col-1 ve-text-center">${level}</span>
<span class="ve-col-1-5 ${Parser.sourceJsonToColor(it.source)} ve-text-center pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>
<div class="ve-flex ve-hidden relative lst__wrp-preview">
<div class="vr-0 absolute lst__vr-preview"></div>

View File

@@ -1574,37 +1574,37 @@ Parser.monCrToFull = function (cr, {xp = null, isMythic = false} = {}) {
}
};
Parser.getFullImmRes = function (toParse) {
Parser.getFullImmRes = function (toParse, {isPlainText = false} = {}) {
if (!toParse?.length) return "";
let maxDepth = 0;
function toString (it, depth = 0) {
const renderString = str => isPlainText ? Renderer.stripTags(`${str}`) : Renderer.get().render(`${str}`);
const render = (val, depth = 0) => {
maxDepth = Math.max(maxDepth, depth);
if (typeof it === "string") {
return it;
} else if (it.special) {
return it.special;
} else {
const stack = [];
if (typeof val === "string") return renderString(val);
if (it.preNote) stack.push(it.preNote);
if (val.special) return renderString(val.special);
const prop = it.immune ? "immune" : it.resist ? "resist" : it.vulnerable ? "vulnerable" : null;
if (prop) {
const toJoin = it[prop].length === Parser.DMG_TYPES.length && CollectionUtil.deepEquals(Parser.DMG_TYPES, it[prop])
? ["all damage"]
: it[prop].map(nxt => toString(nxt, depth + 1));
stack.push(depth ? toJoin.join(maxDepth ? "; " : ", ") : toJoin.joinConjunct(", ", " and "));
}
const stack = [];
if (it.note) stack.push(it.note);
if (val.preNote) stack.push(renderString(val.preNote));
return stack.join(" ");
const prop = val.immune ? "immune" : val.resist ? "resist" : val.vulnerable ? "vulnerable" : null;
if (prop) {
const toJoin = val[prop].length === Parser.DMG_TYPES.length && CollectionUtil.deepEquals(Parser.DMG_TYPES, val[prop])
? ["all damage"]
: val[prop].map(nxt => render(nxt, depth + 1));
stack.push(renderString(depth ? toJoin.join(maxDepth ? "; " : ", ") : toJoin.joinConjunct(", ", " and ")));
}
}
const arr = toParse.map(it => toString(it));
if (val.note) stack.push(renderString(val.note));
return stack.join(" ");
};
const arr = toParse.map(it => render(it));
if (arr.length <= 1) return arr.join("");
@@ -2645,6 +2645,7 @@ Parser.SRC_GHLoE = "GHLoE";
Parser.SRC_DoDk = "DoDk";
Parser.SRC_HWCS = "HWCS";
Parser.SRC_HWAitW = "HWAitW";
Parser.SRC_TD = "TD";
Parser.SRC_SCREEN = "Screen";
Parser.SRC_SCREEN_WILDERNESS_KIT = "ScreenWildernessKit";
Parser.SRC_SCREEN_DUNGEON_KIT = "ScreenDungeonKit";
@@ -2820,6 +2821,7 @@ Parser.SOURCE_JSON_TO_FULL[Parser.SRC_GHLoE] = "Grim Hollow: Lairs of Etharis";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DoDk] = "Dungeons of Drakkenheim";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HWCS] = "Humblewood Campaign Setting";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HWAitW] = "Humblewood: Adventure in the Wood";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TD] = "Tarot Deck";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN] = "Dungeon Master's Screen";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN_WILDERNESS_KIT] = "Dungeon Master's Screen: Wilderness Kit";
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN_DUNGEON_KIT] = "Dungeon Master's Screen: Dungeon Kit";
@@ -2970,6 +2972,7 @@ Parser.SOURCE_JSON_TO_ABV[Parser.SRC_GHLoE] = "GHLoE";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DoDk] = "DoDk";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HWCS] = "HWCS";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HWAitW] = "HWAitW";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TD] = "TD";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN] = "Screen";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN_WILDERNESS_KIT] = "ScWild";
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN_DUNGEON_KIT] = "ScDun";
@@ -3119,6 +3122,7 @@ Parser.SOURCE_JSON_TO_DATE[Parser.SRC_GHLoE] = "2023-11-30";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DoDk] = "2023-12-21";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HWCS] = "2019-06-17";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HWAitW] = "2019-06-17";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TD] = "2022-05-24";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN] = "2015-01-20";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN_WILDERNESS_KIT] = "2020-11-17";
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN_DUNGEON_KIT] = "2020-09-21";
@@ -3307,6 +3311,7 @@ Parser.SOURCES_PARTNERED_WOTC = new Set([
Parser.SRC_DoDk,
Parser.SRC_HWCS,
Parser.SRC_HWAitW,
Parser.SRC_TD,
]);
// region Source categories
@@ -3434,6 +3439,7 @@ Parser.SOURCES_AVAILABLE_DOCS_BOOK = {};
Parser.SRC_BMT,
Parser.SRC_DMTCRG,
Parser.SRC_HWCS,
Parser.SRC_TD,
].forEach(src => {
Parser.SOURCES_AVAILABLE_DOCS_BOOK[src] = src;
Parser.SOURCES_AVAILABLE_DOCS_BOOK[src.toLowerCase()] = src;

View File

@@ -11,17 +11,17 @@ class PsionicsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-6 pl-0",
css: "bold ve-col-6 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Type",
css: "col-3 ve-text-center",
css: "ve-col-3 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Order",
css: "col-3 ve-text-center pr-0",
css: "ve-col-3 ve-text-center pr-0",
colStyle: "text-center",
}),
];
@@ -116,10 +116,10 @@ class PsionicsPage extends ListPage {
const typeMeta = Parser.psiTypeToMeta(p.type);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-6 pl-0">${p.name}</span>
<span class="col-2 ve-text-center">${typeMeta.short}</span>
<span class="col-2 ve-text-center ${p._fOrder === VeCt.STR_NONE ? "list-entry-none" : ""}">${p._fOrder}</span>
<span class="col-2 ve-text-center pr-0" title="${Parser.sourceJsonToFull(p.source)}" ${Parser.sourceJsonToStyle(p.source)}>${source}</span>
<span class="bold ve-col-6 pl-0">${p.name}</span>
<span class="ve-col-2 ve-text-center">${typeMeta.short}</span>
<span class="ve-col-2 ve-text-center ${p._fOrder === VeCt.STR_NONE ? "list-entry-none" : ""}">${p._fOrder}</span>
<span class="ve-col-2 ve-text-center pr-0" title="${Parser.sourceJsonToFull(p.source)}" ${Parser.sourceJsonToStyle(p.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -11,17 +11,17 @@ class RacesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-5 pl-0",
css: "bold ve-col-5 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Ability",
css: "col-5",
css: "ve-col-5",
colStyle: "",
}),
new SublistCellTemplate({
name: "Size",
css: "col-2 ve-text-center pr-0",
css: "ve-col-2 ve-text-center pr-0",
colStyle: "text-center",
}),
];
@@ -112,10 +112,10 @@ class RacesPage extends ListPage {
const source = Parser.sourceJsonToAbv(race.source);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-4 pl-0">${race.name}</span>
<span class="col-4 ${race._slAbility === "Lineage (choose)" ? "italic" : ""}">${race._slAbility}</span>
<span class="col-2 ve-text-center">${size}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(race.source)} pr-0" title="${Parser.sourceJsonToFull(race.source)}" ${Parser.sourceJsonToStyle(race.source)}>${source}</span>
<span class="bold ve-col-4 pl-0">${race.name}</span>
<span class="ve-col-4 ${race._slAbility === "Lineage (choose)" ? "italic" : ""}">${race._slAbility}</span>
<span class="ve-col-2 ve-text-center">${size}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(race.source)} pr-0" title="${Parser.sourceJsonToFull(race.source)}" ${Parser.sourceJsonToStyle(race.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -15,12 +15,12 @@ class RecipesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-9 pl-0",
css: "bold ve-col-9 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Type",
css: "col-3 ve-text-center pr-0",
css: "ve-col-3 ve-text-center pr-0",
colStyle: "text-center",
}),
];
@@ -91,9 +91,9 @@ class RecipesPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(ent);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-6 bold pl-0">${ent.name}</span>
<span class="col-4 ve-text-center">${ent.type || "\u2014"}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(ent.source)} pr-0" title="${Parser.sourceJsonToFull(ent.source)}" ${Parser.sourceJsonToStyle(ent.source)}>${source}</span>
<span class="ve-col-6 bold pl-0">${ent.name}</span>
<span class="ve-col-4 ve-text-center">${ent.type || "\u2014"}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(ent.source)} pr-0" title="${Parser.sourceJsonToFull(ent.source)}" ${Parser.sourceJsonToStyle(ent.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -195,6 +195,8 @@ Renderer.dice = {
// endregion
// region Event handling
RE_PROMPT: /#\$prompt_number:?([^$]*)\$#/g,
async pRollerClickUseData (evt, ele) {
evt.stopPropagation();
evt.preventDefault();
@@ -233,10 +235,8 @@ Renderer.dice = {
if (!chosenRollData) return;
const rePrompt = /#\$prompt_number:?([^$]*)\$#/g;
const results = [];
let m;
while ((m = rePrompt.exec(chosenRollData.toRoll))) {
for (const m of chosenRollData.toRoll.matchAll(Renderer.dice.RE_PROMPT)) {
const optionsRaw = m[1];
const opts = {};
if (optionsRaw) {
@@ -263,8 +263,7 @@ Renderer.dice = {
}
const rollDataCpy = MiscUtil.copyFast(chosenRollData);
rePrompt.lastIndex = 0;
rollDataCpy.toRoll = rollDataCpy.toRoll.replace(rePrompt, () => results.shift());
rollDataCpy.toRoll = rollDataCpy.toRoll.replace(Renderer.dice.RE_PROMPT, () => results.shift());
// If there's a prompt, prompt the user to select the dice
let rollDataCpyToRoll;

View File

@@ -909,9 +909,9 @@ RendererMarkdown.monster = class {
const abilityScorePart = RendererMarkdown.utils.compact.getRenderedAbilityScores(mon, {prefix: ">"});
const savePart = mon.save ? `\n>- **Saving Throws** ${Object.keys(mon.save).sort(SortUtil.ascSortAtts).map(it => RendererMarkdown.monster.getSave(it, mon.save[it])).join(", ")}` : "";
const skillPart = mon.skill ? `\n>- **Skills** ${RendererMarkdown.monster.getSkillsString(mon)}` : "";
const damVulnPart = mon.vulnerable ? `\n>- **Damage Vulnerabilities** ${Parser.getFullImmRes(mon.vulnerable)}` : "";
const damResPart = mon.resist ? `\n>- **Damage Resistances** ${Parser.getFullImmRes(mon.resist)}` : "";
const damImmPart = mon.immune ? `\n>- **Damage Immunities** ${Parser.getFullImmRes(mon.immune)}` : "";
const damVulnPart = mon.vulnerable ? `\n>- **Damage Vulnerabilities** ${Parser.getFullImmRes(mon.vulnerable, {isPlainText: true})}` : "";
const damResPart = mon.resist ? `\n>- **Damage Resistances** ${Parser.getFullImmRes(mon.resist, {isPlainText: true})}` : "";
const damImmPart = mon.immune ? `\n>- **Damage Immunities** ${Parser.getFullImmRes(mon.immune, {isPlainText: true})}` : "";
const condImmPart = mon.conditionImmune ? `\n>- **Condition Immunities** ${Parser.getFullCondImm(mon.conditionImmune, {isPlainText: true})}` : "";
const sensePart = !opts.isHideSenses ? `\n>- **Senses** ${mon.senses ? `${Renderer.utils.getRenderedSenses(mon.senses, true)}, ` : ""}passive Perception ${mon.passive || "\u2014"}` : "";
const languagePart = !opts.isHideLanguages ? `\n>- **Languages** ${Renderer.monster.getRenderedLanguages(mon.languages)}` : "";

View File

@@ -258,7 +258,7 @@ globalThis.Renderer = function () {
MiscUtil.delete(this._plugins, pluginType);
};
this._getPlugins = function (pluginType) { return this._plugins[pluginType] || []; };
this._getPlugins = function (pluginType) { return this._plugins[pluginType] ||= []; };
/** Run a function with the given plugin active. */
this.withPlugin = function ({pluginTypes, fnPlugin, fn}) {
@@ -437,11 +437,15 @@ globalThis.Renderer = function () {
}
};
this._RE_TEXT_CENTER = /\btext-center\b/;
this._RE_TEXT_CENTER = /\btext-center\b/g;
this._RE_COL_D = /\bcol-\d\d?(?:-\d\d?)?\b/g;
this._getMutatedStyleString = function (str) {
if (!str) return str;
return str.replace(this._RE_TEXT_CENTER, "ve-text-center");
return str
.replace(this._RE_TEXT_CENTER, "ve-$&")
.replace(this._RE_COL_D, "ve-$&")
;
};
this._adjustDepth = function (meta, dDepth) {
@@ -1548,13 +1552,28 @@ globalThis.Renderer = function () {
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));
this._renderString_renderTag(textStack, meta, options, tag, text);
} else textStack[0] += s;
if (!s.startsWith("{@")) {
this._renderString_renderBasic(textStack, meta, options, s);
continue;
}
const [tag, text] = Renderer.splitFirstSpace(s.slice(1, -1));
this._renderString_renderTag(textStack, meta, options, tag, text);
}
};
this._renderString_renderBasic = function (textStack, meta, options, str) {
// region Plugins
for (const plugin of this._getPlugins("string_basic")) {
const out = plugin(str, textStack, meta, options);
if (out) return void (textStack[0] += out);
}
// endregion
textStack[0] += str;
};
this._renderString_renderTag = function (textStack, meta, options, tag, text) {
// region Plugins
// Generic
@@ -1791,7 +1810,11 @@ globalThis.Renderer = function () {
case "@link": {
const [displayText, url] = Renderer.splitTagByPipe(text);
let outUrl = url == null ? displayText : url;
if (!outUrl.startsWith("http")) outUrl = `http://${outUrl}`; // avoid HTTPS, as the D&D homepage doesn't support it
// If a URL is prefixed with e.g. `https://` or `mailto:`, leave it as-is
// Otherwise, assume `http` (avoid HTTPS, as the D&D homepage doesn't support it)
if (!/^[a-zA-Z]+:/.test(outUrl)) outUrl = `http://${outUrl}`;
const fauxEntry = {
type: "link",
href: {
@@ -2046,7 +2069,21 @@ globalThis.Renderer = function () {
const replacementAttributes = pluginData.map(it => it.attributesHoverReplace).filter(Boolean);
if (replacementAttributes.length) return replacementAttributes.join(" ");
return `onmouseover="Renderer.hover.pHandleLinkMouseOver(event, this)" onmouseleave="Renderer.hover.handleLinkMouseLeave(event, this)" onmousemove="Renderer.hover.handleLinkMouseMove(event, this)" ondragstart="Renderer.hover.handleLinkDragStart(event, this)" data-vet-page="${entry.href.hover.page.qq()}" data-vet-source="${entry.href.hover.source.qq()}" data-vet-hash="${procHash.qq()}" ${entry.href.hover.preloadId != null ? `data-vet-preload-id="${`${entry.href.hover.preloadId}`.qq()}"` : ""} ${entry.href.hover.isFauxPage ? `data-vet-is-faux-page="true"` : ""} ${Renderer.hover.getPreventTouchString()}`;
return [
`onmouseover="Renderer.hover.pHandleLinkMouseOver(event, this)"`,
`onmouseleave="Renderer.hover.handleLinkMouseLeave(event, this)"`,
`onmousemove="Renderer.hover.handleLinkMouseMove(event, this)"`,
`onclick="Renderer.hover.handleLinkClick(event, this)"`,
`ondragstart="Renderer.hover.handleLinkDragStart(event, this)"`,
`data-vet-page="${entry.href.hover.page.qq()}"`,
`data-vet-source="${entry.href.hover.source.qq()}"`,
`data-vet-hash="${procHash.qq()}"`,
entry.href.hover.preloadId != null ? `data-vet-preload-id="${`${entry.href.hover.preloadId}`.qq()}"` : "",
entry.href.hover.isFauxPage ? `data-vet-is-faux-page="true"` : "",
Renderer.hover.getPreventTouchString(),
]
.filter(Boolean)
.join(" ");
};
/**
@@ -2099,8 +2136,12 @@ Renderer.get = () => {
return Renderer.defaultRenderer;
};
/**
* Note that a tag (`{@tag ...}`) is not valid inside a property injector (`{=prop ...}`),
* but a property injector *is* valid inside a tag.
*/
Renderer.applyProperties = function (entry, object) {
const propSplit = Renderer.splitByPropertyInjectors(entry);
const propSplit = Renderer.splitByTags(entry);
const len = propSplit.length;
if (len === 1) return entry;
@@ -2110,6 +2151,12 @@ Renderer.applyProperties = function (entry, object) {
const s = propSplit[i];
if (!s) continue;
if (s.startsWith("{@")) {
const [tag, text] = Renderer.splitFirstSpace(s.slice(1, -1));
textStack += `{${tag} ${Renderer.applyProperties(text, object)}}`;
continue;
}
if (!s.startsWith("{=")) {
textStack += s;
continue;
@@ -2198,61 +2245,69 @@ Renderer.splitFirstSpace = function (string) {
return firstIndex === -1 ? [string, ""] : [string.substr(0, firstIndex), string.substr(firstIndex + 1)];
};
Renderer._splitByTagsBase = function (leadingCharacter) {
return function (string) {
let tagDepth = 0;
let char, char2;
const out = [];
let curStr = "";
let isLastOpen = false;
Renderer._SPLIT_BY_TAG_LEADING_CHARS = new Set(["@", "="]);
const len = string.length;
for (let i = 0; i < len; ++i) {
char = string[i];
char2 = string[i + 1];
Renderer.splitByTags = function (string) {
let tagDepth = 0;
let char, char2;
const out = [];
let curStr = "";
let isPrevCharOpenBrace = false;
switch (char) {
case "{":
isLastOpen = true;
if (char2 === leadingCharacter) {
if (tagDepth++ > 0) {
curStr += "{";
} else {
out.push(curStr.replace(/<VE_LEAD>/g, leadingCharacter));
curStr = `{${leadingCharacter}`;
++i;
}
} else curStr += "{";
break;
const pushOutput = () => {
if (!curStr) return;
out.push(curStr);
};
case "}":
isLastOpen = false;
curStr += "}";
if (tagDepth !== 0 && --tagDepth === 0) {
out.push(curStr.replace(/<VE_LEAD>/g, leadingCharacter));
curStr = "";
}
break;
const len = string.length;
for (let i = 0; i < len; ++i) {
char = string[i];
char2 = string[i + 1];
case leadingCharacter: {
if (!isLastOpen) curStr += "<VE_LEAD>";
else curStr += leadingCharacter;
switch (char) {
case "{":
if (!Renderer._SPLIT_BY_TAG_LEADING_CHARS.has(char2)) {
isPrevCharOpenBrace = false;
curStr += "{";
break;
}
default: isLastOpen = false; curStr += char; break;
isPrevCharOpenBrace = true;
if (tagDepth++ > 0) {
curStr += "{";
} else {
pushOutput();
curStr = `{${char2}`;
++i;
}
break;
case "}":
isPrevCharOpenBrace = false;
curStr += "}";
if (tagDepth !== 0 && --tagDepth === 0) {
pushOutput();
curStr = "";
}
break;
case "@":
case "=": {
curStr += char;
break;
}
default: isPrevCharOpenBrace = false; curStr += char; break;
}
}
if (curStr) out.push(curStr.replace(/<VE_LEAD>/g, leadingCharacter));
pushOutput();
return out;
};
return out;
};
Renderer.splitByTags = Renderer._splitByTagsBase("@");
Renderer.splitByPropertyInjectors = Renderer._splitByTagsBase("=");
Renderer._splitByPipeBase = function (leadingCharacter) {
return function (string) {
let tagDepth = 0;
@@ -4218,7 +4273,7 @@ Renderer.tag = class {
get tag () { return `@${this.tagName}`; }
getStripped (tag, text) {
text = text.replace(/<\$([^$]+)\$>/gi, ""); // remove any variable tags
text = DataUtil.generic.variableResolver.getHumanReadableString(text); // replace any variables
return this._getStripped(tag, text);
}
@@ -6163,9 +6218,9 @@ Renderer.race = class {
<tr><td colspan="6">
<table class="w-100 summary stripe-even-table">
<tr>
<th class="col-4 ve-text-center">Ability Scores</th>
<th class="col-4 ve-text-center">Size</th>
<th class="col-4 ve-text-center">Speed</th>
<th class="ve-col-4 ve-text-center">Ability Scores</th>
<th class="ve-col-4 ve-text-center">Size</th>
<th class="ve-col-4 ve-text-center">Speed</th>
</tr>
<tr>
<td class="ve-text-center">${Renderer.getAbilityData(race.ability).asText}</td>
@@ -6197,7 +6252,7 @@ Renderer.race = class {
static getHeightAndWeightEntries (race, {isStatic = false} = {}) {
const colLabels = ["Base Height", "Base Weight", "Height Modifier", "Weight Modifier"];
const colStyles = ["col-2-3 ve-text-center", "col-2-3 ve-text-center", "col-2-3 ve-text-center", "col-2 ve-text-center"];
const colStyles = ["col-2-3 text-center", "col-2-3 text-center", "col-2-3 text-center", "col-2 text-center"];
const cellHeightMod = !isStatic
? `+<span data-race-heightmod="true">${race.heightAndWeight.heightMod}</span>`
@@ -6215,7 +6270,7 @@ Renderer.race = class {
if (!isStatic) {
colLabels.push("");
colStyles.push("col-3-1 ve-text-center");
colStyles.push("ve-col-3-1 text-center");
row.push(`<div class="ve-flex-vh-center">
<div class="ve-hidden race__disp-result-height-weight ve-flex-v-baseline">
<div class="mr-1">=</div>
@@ -7685,7 +7740,7 @@ Renderer.monster = class {
const absRemaining = Parser.ABIL_ABVS.filter(ab => !seenAbs.has(ab));
return `<tr>
${absRemaining.map(ab => `<th class="col-2 ve-text-center bold">${ab.toUpperCase()}</th>`).join("")}
${absRemaining.map(ab => `<th class="ve-col-2 ve-text-center bold">${ab.toUpperCase()}</th>`).join("")}
</tr>
<tr>
${absRemaining.map(ab => `<td class="ve-text-center">${Renderer.utils.getAbilityRoller(mon, ab)}</td>`).join("")}
@@ -8180,7 +8235,7 @@ Renderer.item = class {
const dexterityMax = (itemType === "MA" && item.dexterityMax == null)
? 2
: item.dexterityMax;
const isAddDex = item.dexterityMax != null || itemType !== "HA";
const isAddDex = item.dexterityMax != null || !["HA", "S"].includes(itemType);
const prefix = item.type === "S" ? "+" : "";
const suffix = isAddDex ? ` + Dex${dexterityMax ? ` (max ${dexterityMax})` : ""}` : "";
@@ -9862,12 +9917,12 @@ Renderer.vehicle = class {
return Parser.ABIL_ABVS.some(it => veh[it] != null) ? `<tr><td colspan="6">
<table class="w-100 summary stripe-even-table">
<tr>
<th class="col-2 ve-text-center">STR</th>
<th class="col-2 ve-text-center">DEX</th>
<th class="col-2 ve-text-center">CON</th>
<th class="col-2 ve-text-center">INT</th>
<th class="col-2 ve-text-center">WIS</th>
<th class="col-2 ve-text-center">CHA</th>
<th class="ve-col-2 ve-text-center">STR</th>
<th class="ve-col-2 ve-text-center">DEX</th>
<th class="ve-col-2 ve-text-center">CON</th>
<th class="ve-col-2 ve-text-center">INT</th>
<th class="ve-col-2 ve-text-center">WIS</th>
<th class="ve-col-2 ve-text-center">CHA</th>
</tr>
<tr>
<td class="ve-text-center">${Renderer.utils.getAbilityRoller(veh, "str")}</td>
@@ -11033,16 +11088,16 @@ Renderer.hover = class {
static cleanTempWindows () {
for (const [key, meta] of Renderer.hover._eleCache.entries()) {
// If this is an element-less "permanent" show which has been closed
// If this is an element-less "permanent" show (i.e. a "predefined" window) which has been closed
if (!meta.isPermanent && meta.windowMeta && typeof key === "number") {
meta.windowMeta.doClose();
Renderer.hover._eleCache.delete(key);
return;
continue;
}
if (!meta.isPermanent && meta.windowMeta && !document.body.contains(key)) {
meta.windowMeta.doClose();
return;
continue;
}
if (!meta.isPermanent && meta.isHovered && meta.windowMeta) {
@@ -11064,9 +11119,9 @@ Renderer.hover = class {
.forEach(([, meta]) => meta.doClose());
}
static _getSetMeta (ele) {
if (!Renderer.hover._eleCache.has(ele)) Renderer.hover._eleCache.set(ele, new Renderer.hover.LinkMeta());
return Renderer.hover._eleCache.get(ele);
static _getSetMeta (key) {
if (!Renderer.hover._eleCache.has(key)) Renderer.hover._eleCache.set(key, new Renderer.hover.LinkMeta());
return Renderer.hover._eleCache.get(key);
}
static _handleGenericMouseOverStart ({evt, ele}) {
@@ -11367,6 +11422,12 @@ Renderer.hover = class {
ele.style.cursor = "";
}
static handleLinkClick (evt, ele) {
// Close the window (if not permanent)
// Note that this prevents orphan windows when e.g. clicking a specific variant on an Items page magicvariant
Renderer.hover.handleLinkMouseLeave(evt, ele);
}
// (Baked into render strings)
static handleLinkDragStart (evt, ele) {
// Close the window
@@ -12416,28 +12477,47 @@ Renderer.findName = function (entry) { return CollectionUtil.dfs(entry, {prop: "
Renderer.findSource = function (entry) { return CollectionUtil.dfs(entry, {prop: "source"}); };
Renderer.findEntry = function (entry) { return CollectionUtil.dfs(entry, {fnMatch: obj => obj.name && obj?.entries?.length}); };
Renderer.stripTags = function (str) {
/**
* @param {string} str
* @param {?Set<string>} allowlistTags
* @param {?Set<string>} blocklistTags
*/
Renderer.stripTags = function (str, {allowlistTags = null, blocklistTags = null} = {}) {
if (!str) return str;
let nxtStr = Renderer._stripTagLayer(str);
while (nxtStr.length !== str.length) {
str = nxtStr;
nxtStr = Renderer._stripTagLayer(str);
}
return nxtStr;
const ptrAccum = {_: ""};
Renderer._stripTags_textRender({str, ptrAccum, allowlistTags, blocklistTags});
return ptrAccum._;
};
Renderer._stripTagLayer = function (str) {
if (str.includes("{@")) {
const tagSplit = Renderer.splitByTags(str);
return tagSplit.filter(it => it).map(it => {
if (it.startsWith("{@")) {
let [tag, text] = Renderer.splitFirstSpace(it.slice(1, -1));
const tagInfo = Renderer.tag.TAG_LOOKUP[tag];
if (!tagInfo) throw new Error(`Unhandled tag: "${tag}"`);
return tagInfo.getStripped(tag, text);
} else return it;
}).join("");
} return str;
Renderer._stripTags_textRender = function ({str, ptrAccum, allowlistTags = null, blocklistTags = null} = {}) {
const tagSplit = Renderer.splitByTags(str);
const len = tagSplit.length;
for (let i = 0; i < len; ++i) {
const s = tagSplit[i];
if (!s) continue;
if (!s.startsWith("{@")) {
ptrAccum._ += s;
continue;
}
const [tag, text] = Renderer.splitFirstSpace(s.slice(1, -1));
if (
(allowlistTags != null && allowlistTags.has(tag))
|| (blocklistTags != null && !blocklistTags.has(tag))
) {
ptrAccum._ += s;
continue;
}
const tagInfo = Renderer.tag.TAG_LOOKUP[tag];
if (!tagInfo) throw new Error(`Unhandled tag: "${tag}"`);
const stripped = tagInfo.getStripped(tag, text);
Renderer._stripTags_textRender({str: stripped, ptrAccum, allowlistTags, blocklistTags});
}
};
/**

View File

@@ -11,12 +11,12 @@ class RewardsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Type",
css: "col-2 pl-0 ve-text-center",
css: "ve-col-2 pl-0 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Name",
css: "bold col-10 pr-0",
css: "bold ve-col-10 pr-0",
colStyle: "",
}),
];
@@ -81,10 +81,10 @@ class RewardsPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(reward);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="col-2 ve-text-center px-1">${reward.type}</span>
<span class="bold col-7-7">${reward.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(reward.source)} pr-0" title="${Parser.sourceJsonToFull(reward.source)}" ${Parser.sourceJsonToStyle(reward.source)}>${source}</span>
<span class="ve-col-0-3 px-0 ve-flex-vh-center lst__btn-toggle-expand ve-self-flex-stretch">[+]</span>
<span class="ve-col-2 ve-text-center px-1">${reward.type}</span>
<span class="bold ve-col-7-7">${reward.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(reward.source)} pr-0" title="${Parser.sourceJsonToFull(reward.source)}" ${Parser.sourceJsonToStyle(reward.source)}>${source}</span>
</a>
<div class="ve-flex ve-hidden relative lst__wrp-preview">
<div class="vr-0 absolute lst__vr-preview"></div>

View File

@@ -7,7 +7,7 @@ const onLoadSeo = async () => {
document.title = `${it.name} - 5etools`;
$(`.page__title`).text(`${_SEO_PAGE.toTitleCase()}: ${it.name}`);
$(`<div class="col-12 ve-flex-vh-center my-2 pt-3">
$(`<div class="ve-col-12 ve-flex-vh-center my-2 pt-3">
<button class="btn btn-primary">
<a href="/${_SEO_PAGE}.html" style="font-size: 1.7em; color: white;">${_SEO_STYLE === 1 ? `View All` : `View Complete`} ${_SEO_PAGE.toTitleCase()}</a>
</button>

View File

@@ -14,32 +14,32 @@ class SpellsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-3-2 pl-0",
css: "bold ve-col-3-2 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Level",
css: "capitalize col-1-5 ve-text-center",
css: "capitalize ve-col-1-5 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Time",
css: "col-1-8 ve-text-center",
css: "ve-col-1-8 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "School",
css: "capitalize col-1-6 ve-text-center",
css: "capitalize ve-col-1-6 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "C.",
css: "concentration--sublist col-0-7 ve-text-center",
css: "concentration--sublist ve-col-0-7 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Range",
css: "range col-3-2 pr-0 text-right",
css: "range ve-col-3-2 pr-0 text-right",
colStyle: "text-right",
}),
];
@@ -331,21 +331,21 @@ class SpellsPage extends ListPageMultiSource {
href: `#${hash}`,
clazz: "lst--border lst__row-inner",
children: [
e_({tag: "span", clazz: `bold col-2-9 pl-0`, text: spell.name}),
e_({tag: "span", clazz: `col-1-5 ve-text-center`, text: PageFilterSpells.getTblLevelStr(spell)}),
e_({tag: "span", clazz: `col-1-7 ve-text-center`, text: time}),
e_({tag: "span", clazz: `bold ve-col-2-9 pl-0`, text: spell.name}),
e_({tag: "span", clazz: `ve-col-1-5 ve-text-center`, text: PageFilterSpells.getTblLevelStr(spell)}),
e_({tag: "span", clazz: `ve-col-1-7 ve-text-center`, text: time}),
e_({
tag: "span",
clazz: `col-1-2 sp__school-${spell.school} ve-text-center`,
clazz: `ve-col-1-2 sp__school-${spell.school} ve-text-center`,
title: Parser.spSchoolAndSubschoolsAbvsToFull(spell.school, spell.subschools),
style: Parser.spSchoolAbvToStylePart(spell.school),
text: school,
}),
e_({tag: "span", clazz: `col-0-6 ve-text-center`, title: "Concentration", text: concentration}),
e_({tag: "span", clazz: `col-2-4 text-right`, text: range}),
e_({tag: "span", clazz: `ve-col-0-6 ve-text-center`, title: "Concentration", text: concentration}),
e_({tag: "span", clazz: `ve-col-2-4 text-right`, text: range}),
e_({
tag: "span",
clazz: `col-1-7 ve-text-center ${Parser.sourceJsonToColor(spell.source)} pr-0`,
clazz: `ve-col-1-7 ve-text-center ${Parser.sourceJsonToColor(spell.source)} pr-0`,
style: Parser.sourceJsonToStylePart(spell.source),
title: `${Parser.sourceJsonToFull(spell.source)}${Renderer.utils.getSourceSubText(spell)}`,
text: source,

View File

@@ -14,7 +14,7 @@ class TablesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-12 px-0",
css: "bold ve-col-12 px-0",
colStyle: "",
}),
];
@@ -119,8 +119,8 @@ class TablesPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(it);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-10 pl-0">${it.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="bold ve-col-10 pl-0">${it.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -5,12 +5,12 @@ class TrapsHazardsSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Type",
css: "col-4 ve-text-center pl-0",
css: "ve-col-4 ve-text-center pl-0",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Name",
css: "bold col-8 pr-0",
css: "bold ve-col-8 pr-0",
colStyle: "",
}),
];
@@ -75,9 +75,9 @@ class TrapsHazardsPage extends ListPage {
const trapType = Parser.trapHazTypeToFull(it.trapHazType);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="col-3 pl-0 ve-text-center">${trapType}</span>
<span class="bold col-7">${it.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-3 pl-0 ve-text-center">${trapType}</span>
<span class="bold ve-col-7">${it.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -655,18 +655,18 @@ class _BrewUtil2Base {
/* -------------------------------------------- */
async _pGetBrewDependencies ({brewDocs, brewsRaw = null, brewsRawLocal = null, lockToken}) {
async _pGetBrewDependencies ({brewDocs, brewsRaw = null, brewsRawLocal = null, isIgnoreNetworkErrors = false, lockToken}) {
try {
lockToken = await this._LOCK.pLock({token: lockToken});
return (await this._pGetBrewDependencies_({brewDocs, brewsRaw, brewsRawLocal, lockToken}));
return (await this._pGetBrewDependencies_({brewDocs, brewsRaw, brewsRawLocal, isIgnoreNetworkErrors, lockToken}));
} finally {
this._LOCK.unlock();
}
}
async _pGetBrewDependencies_ ({brewDocs, brewsRaw = null, brewsRawLocal = null, lockToken}) {
async _pGetBrewDependencies_ ({brewDocs, brewsRaw = null, brewsRawLocal = null, isIgnoreNetworkErrors = false, lockToken}) {
const urlRoot = await this.pGetCustomUrl();
const brewIndex = await this.pGetSourceIndex(urlRoot);
const brewIndex = await this._pGetBrewDependencies_getBrewIndex({urlRoot, isIgnoreNetworkErrors});
const toLoadSources = [];
const loadedSources = new Set();
@@ -710,6 +710,16 @@ class _BrewUtil2Base {
};
}
async _pGetBrewDependencies_getBrewIndex ({urlRoot, isIgnoreNetworkErrors = false}) {
try {
return (await this.pGetSourceIndex(urlRoot));
} catch (e) {
// Support limited use for e.g. offline file uploads
if (isIgnoreNetworkErrors) return {};
throw e;
}
}
async pGetSourceUrl (source) {
const urlRoot = await this.pGetCustomUrl();
const brewIndex = await this.pGetSourceIndex(urlRoot);
@@ -839,7 +849,7 @@ class _BrewUtil2Base {
const brews = MiscUtil.copyFast(await this._pGetBrewRaw({lockToken}));
const {brewDocsDependencies, unavailableSources} = await this._pGetBrewDependencies({brewDocs, brewsRaw: brews, lockToken});
const {brewDocsDependencies, unavailableSources} = await this._pGetBrewDependencies({brewDocs, brewsRaw: brews, isIgnoreNetworkErrors: true, lockToken});
brewDocs.push(...brewDocsDependencies);
const brewsNxt = this._getNextBrews(brews, brewDocs);
@@ -1898,12 +1908,12 @@ class ManageBrewUi {
});
const $wrpBtnsSort = $$`<div class="filtertools manbrew__filtertools btn-group input-group input-group--bottom ve-flex no-shrink">
<label class="col-0-5 pr-0 btn btn-default btn-xs ve-flex-vh-center">${$cbAll}</label>
<button class="col-1 btn btn-default btn-xs" disabled>Type</button>
<button class="col-3 btn btn-default btn-xs" data-sort="source">Source</button>
<button class="col-3 btn btn-default btn-xs" data-sort="authors">Authors</button>
<button class="col-3 btn btn-default btn-xs" disabled>Origin</button>
<button class="col-1-5 btn btn-default btn-xs ve-grow" disabled>&nbsp;</button>
<label class="ve-col-0-5 pr-0 btn btn-default btn-xs ve-flex-vh-center">${$cbAll}</label>
<button class="ve-col-1 btn btn-default btn-xs" disabled>Type</button>
<button class="ve-col-3 btn btn-default btn-xs" data-sort="source">Source</button>
<button class="ve-col-3 btn btn-default btn-xs" data-sort="authors">Authors</button>
<button class="ve-col-3 btn btn-default btn-xs" disabled>Origin</button>
<button class="ve-col-1-5 btn btn-default btn-xs ve-grow" disabled>&nbsp;</button>
</div>`;
$$(rdState.$stgBrewList)`
@@ -2039,7 +2049,7 @@ class ManageBrewUi {
const lnkUrl = brewSource.url
? e_({
tag: "a",
clazz: "col-2 ve-text-center",
clazz: "ve-col-2 ve-text-center",
href: brewSource.url,
attrs: {
target: "_blank",
@@ -2049,7 +2059,7 @@ class ManageBrewUi {
})
: e_({
tag: "span",
clazz: "col-2 ve-text-center",
clazz: "ve-col-2 ve-text-center",
});
const eleRow = e_({
@@ -2058,12 +2068,12 @@ class ManageBrewUi {
children: [
e_({
tag: "span",
clazz: `col-4 manbrew__source px-1`,
clazz: `ve-col-4 manbrew__source px-1`,
text: brewSource.full,
}),
e_({
tag: "span",
clazz: `col-4 px-1`,
clazz: `ve-col-4 px-1`,
text: authorsFull,
}),
lnkUrl,
@@ -2169,23 +2179,23 @@ class ManageBrewUi {
children: [
e_({
tag: "label",
clazz: `col-0-5 ve-flex-vh-center ve-self-flex-stretch`,
clazz: `ve-col-0-5 ve-flex-vh-center ve-self-flex-stretch`,
children: [cbSel],
}),
e_({
tag: "div",
clazz: `col-1 ve-text-center italic mobile__text-clip-ellipsis`,
clazz: `ve-col-1 ve-text-center italic mobile__text-clip-ellipsis`,
title: ptCategory.title,
text: ptCategory.short,
}),
e_({
tag: "div",
clazz: `col-9 ve-flex-col`,
clazz: `ve-col-9 ve-flex-col`,
children: elesSub,
}),
e_({
tag: "div",
clazz: `col-1-5 btn-group ve-flex-vh-center`,
clazz: `ve-col-1-5 btn-group ve-flex-vh-center`,
children: [
btnPull,
btnEdit,
@@ -2650,12 +2660,12 @@ class GetBrewUi {
async pInit () {
const urlRoot = await this._brewUtil.pGetCustomUrl();
const [timestamps, propIndex, metaIndex, sourceIndex] = await Promise.all([
this._brewUtil.pLoadTimestamps(urlRoot),
this._brewUtil.pLoadPropIndex(urlRoot),
this._brewUtil.pLoadMetaIndex(urlRoot),
this._brewUtil.pGetSourceIndex(urlRoot),
]);
const indexes = await this._pInit_pGetIndexes({urlRoot});
// Tolerate e.g. opening when offline
if (!indexes) return null;
const {timestamps, propIndex, metaIndex, sourceIndex} = indexes;
const pathToMeta = {};
Object.entries(propIndex)
@@ -2705,6 +2715,27 @@ class GetBrewUi {
.sort((a, b) => SortUtil.ascSortLower(a._brewName, b._brewName));
}
async _pInit_pGetIndexes ({urlRoot}) {
try {
const [timestamps, propIndex, metaIndex, sourceIndex] = await Promise.all([
this._brewUtil.pLoadTimestamps(urlRoot),
this._brewUtil.pLoadPropIndex(urlRoot),
this._brewUtil.pLoadMetaIndex(urlRoot),
this._brewUtil.pGetSourceIndex(urlRoot),
]);
return {
timestamps,
propIndex,
metaIndex,
sourceIndex,
};
} catch (e) {
JqueryUtil.doToast({content: `Failed to load ${this._brewUtil.DISPLAY_NAME} indexes! ${VeCt.STR_SEE_CONSOLE}`, type: "danger"});
setTimeout(() => { throw e; });
return null;
}
}
async pHandlePreCloseModal ({rdState}) {
// region If the user has selected list items, prompt to load them before closing the modal
const cntSel = rdState.list.items.filter(it => it.data.cbSel.checked).length;
@@ -2727,7 +2758,7 @@ class GetBrewUi {
rdState.pageFilter = new this.constructor._PageFilterGetBrew({brewUtil: this._brewUtil});
const $btnAddSelected = $(`<button class="btn ${this._brewUtil.STYLE_BTN} btn-sm col-0-5 ve-text-center" disabled title="Add Selected"><span class="glyphicon glyphicon-save"></button>`);
const $btnAddSelected = $(`<button class="btn ${this._brewUtil.STYLE_BTN} btn-sm ve-col-0-5 ve-text-center" disabled title="Add Selected"><span class="glyphicon glyphicon-save"></button>`);
const $wrpRows = $$`<div class="list smooth-scroll max-h-unset"><div class="lst__row ve-flex-col"><div class="lst__wrp-cells lst--border lst__row-inner ve-flex w-100"><i>Loading...</i></div></div></div>`;
@@ -2749,15 +2780,15 @@ class GetBrewUi {
const $wrpMiniPills = $(`<div class="fltr__mini-view btn-group"></div>`);
const btnSortAddedPublished = this._brewUtil.IS_PREFER_DATE_ADDED
? `<button class="col-1-4 sort btn btn-default btn-xs" data-sort="added">Added</button>`
: `<button class="col-1-4 sort btn btn-default btn-xs" data-sort="published">Published</button>`;
? `<button class="ve-col-1-4 sort btn btn-default btn-xs" data-sort="added">Added</button>`
: `<button class="ve-col-1-4 sort btn btn-default btn-xs" data-sort="published">Published</button>`;
const $wrpSort = $$`<div class="filtertools manbrew__filtertools btn-group input-group input-group--bottom ve-flex no-shrink">
<label class="col-0-5 pr-0 btn btn-default btn-xs ve-flex-vh-center">${rdState.cbAll}</label>
<button class="col-3-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="col-3 sort btn btn-default btn-xs" data-sort="author">Author</button>
<button class="col-1-2 sort btn btn-default btn-xs" data-sort="category">Category</button>
<button class="col-1-4 sort btn btn-default btn-xs" data-sort="modified">Modified</button>
<label class="ve-col-0-5 pr-0 btn btn-default btn-xs ve-flex-vh-center">${rdState.cbAll}</label>
<button class="ve-col-3-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="ve-col-3 sort btn btn-default btn-xs" data-sort="author">Author</button>
<button class="ve-col-1-2 sort btn btn-default btn-xs" data-sort="category">Category</button>
<button class="ve-col-1-4 sort btn btn-default btn-xs" data-sort="modified">Modified</button>
${btnSortAddedPublished}
<button class="sort btn btn-default btn-xs ve-grow" disabled>Source</button>
</div>`;
@@ -2853,7 +2884,7 @@ class GetBrewUi {
const btnAdd = e_({
tag: "span",
clazz: `col-3-5 bold manbrew__load_from_url pl-0 clickable`,
clazz: `ve-col-3-5 bold manbrew__load_from_url pl-0 clickable`,
text: brewInfo._brewName,
click: evt => this._pHandleClick_btnGetRemote({evt, btn: btnAdd, url: brewInfo.download_url}),
});
@@ -2868,17 +2899,17 @@ class GetBrewUi {
children: [
e_({
tag: "label",
clazz: `col-0-5 ve-flex-vh-center ve-self-flex-stretch`,
clazz: `ve-col-0-5 ve-flex-vh-center ve-self-flex-stretch`,
children: [cbSel],
}),
btnAdd,
e_({tag: "span", clazz: "col-3", text: brewInfo._brewAuthor}),
e_({tag: "span", clazz: "col-1-2 ve-text-center mobile__text-clip-ellipsis", text: brewInfo._brewPropDisplayName, title: brewInfo._brewPropDisplayName}),
e_({tag: "span", clazz: "col-1-4 ve-text-center code", text: timestampModified}),
e_({tag: "span", clazz: "col-1-4 ve-text-center code", text: timestampAddedPublished}),
e_({tag: "span", clazz: "ve-col-3", text: brewInfo._brewAuthor}),
e_({tag: "span", clazz: "ve-col-1-2 ve-text-center mobile__text-clip-ellipsis", text: brewInfo._brewPropDisplayName, title: brewInfo._brewPropDisplayName}),
e_({tag: "span", clazz: "ve-col-1-4 ve-text-center code", text: timestampModified}),
e_({tag: "span", clazz: "ve-col-1-4 ve-text-center code", text: timestampAddedPublished}),
e_({
tag: "span",
clazz: "col-1 manbrew__source ve-text-center pr-0",
clazz: "ve-col-1 manbrew__source ve-text-center pr-0",
children: [
e_({
tag: "a",
@@ -3223,10 +3254,10 @@ class ManageEditableBrewContentsUi extends BaseComponent {
const $iptSearch = $(`<input type="search" class="search manbrew__search form-control w-100 lst__search lst__search--no-border-h" placeholder="Search entries...">`);
const $dispCntVisible = $(`<div class="lst__wrp-search-visible no-events ve-flex-vh-center"></div>`);
const $wrpBtnsSort = $$`<div class="filtertools manbrew__filtertools input-group input-group--bottom ve-flex no-shrink">
<label class="btn btn-default btn-xs col-1 pr-0 ve-flex-vh-center">${$cbAll}</label>
<button class="col-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="col-1 sort btn btn-default btn-xs" data-sort="source">Source</button>
<button class="col-5 sort btn btn-default btn-xs" data-sort="category">Category</button>
<label class="btn btn-default btn-xs ve-col-1 pr-0 ve-flex-vh-center">${$cbAll}</label>
<button class="ve-col-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="ve-col-1 sort btn btn-default btn-xs" data-sort="source">Source</button>
<button class="ve-col-5 sort btn btn-default btn-xs" data-sort="category">Category</button>
</div>`;
$$(tabMeta.$wrpTab)`
@@ -3309,10 +3340,10 @@ class ManageEditableBrewContentsUi extends BaseComponent {
const dispProp = this.constructor._getDisplayProp({ent, prop});
eleLi.innerHTML = `<label class="lst--border lst__row-inner no-select mb-0 ve-flex-v-center">
<div class="pl-0 col-1 ve-flex-vh-center"><input type="checkbox" class="no-events"></div>
<div class="col-5 bold">${dispName}</div>
<div class="col-1 ve-text-center" title="${(sourceMeta.full || "").qq()}" ${this._brewUtil.sourceToStyle(sourceMeta)}>${sourceMeta.abbreviation}</div>
<div class="col-5 ve-flex-vh-center pr-0">${dispProp}</div>
<div class="pl-0 ve-col-1 ve-flex-vh-center"><input type="checkbox" class="no-events"></div>
<div class="ve-col-5 bold">${dispName}</div>
<div class="ve-col-1 ve-text-center" title="${(sourceMeta.full || "").qq()}" ${this._brewUtil.sourceToStyle(sourceMeta)}>${sourceMeta.abbreviation}</div>
<div class="ve-col-5 ve-flex-vh-center pr-0">${dispProp}</div>
</label>`;
const listItem = new ListItem(
@@ -3378,8 +3409,8 @@ class ManageEditableBrewContentsUi extends BaseComponent {
const $row = $$`<div class="lst__row ve-flex-col px-0">
<div class="split-v-center lst--border lst__row-inner no-select mb-0 ve-flex-v-center">
<div class="col-10">${displayFn(this._brew, prop, k)}</div>
<div class="col-2 btn-group ve-flex-v-center ve-flex-h-right">
<div class="ve-col-10">${displayFn(this._brew, prop, k)}</div>
<div class="ve-col-2 btn-group ve-flex-v-center ve-flex-h-right">
${$btnDelete}
</div>
</div>
@@ -3403,10 +3434,10 @@ class ManageEditableBrewContentsUi extends BaseComponent {
const $wrpRows = $$`<div class="list ve-flex-col w-100 max-h-unset"></div>`;
const $iptSearch = $(`<input type="search" class="search manbrew__search form-control w-100 mt-1" placeholder="Search source...">`);
const $wrpBtnsSort = $$`<div class="filtertools manbrew__filtertools input-group input-group--bottom ve-flex no-shrink">
<label class="btn btn-default btn-xs col-1 pr-0 ve-flex-vh-center">${$cbAll}</label>
<button class="col-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="col-2 sort btn btn-default btn-xs" data-sort="abbreviation">Abbreviation</button>
<button class="col-4 sort btn btn-default btn-xs" data-sort="json">JSON</button>
<label class="btn btn-default btn-xs ve-col-1 pr-0 ve-flex-vh-center">${$cbAll}</label>
<button class="ve-col-5 sort btn btn-default btn-xs" data-sort="name">Name</button>
<button class="ve-col-2 sort btn btn-default btn-xs" data-sort="abbreviation">Abbreviation</button>
<button class="ve-col-4 sort btn btn-default btn-xs" data-sort="json">JSON</button>
</div>`;
$$(tabMeta.$wrpTab)`
@@ -3442,10 +3473,10 @@ class ManageEditableBrewContentsUi extends BaseComponent {
const abv = source.abbreviation || _BrewInternalUtil.SOURCE_UNKNOWN_ABBREVIATION;
eleLi.innerHTML = `<label class="lst--border lst__row-inner no-select mb-0 ve-flex-v-center">
<div class="pl-0 col-1 ve-flex-vh-center"><input type="checkbox" class="no-events"></div>
<div class="col-5 bold">${name}</div>
<div class="col-2 ve-text-center">${abv}</div>
<div class="col-4 ve-flex-vh-center pr-0">${source.json}</div>
<div class="pl-0 ve-col-1 ve-flex-vh-center"><input type="checkbox" class="no-events"></div>
<div class="ve-col-5 bold">${name}</div>
<div class="ve-col-2 ve-text-center">${abv}</div>
<div class="ve-col-4 ve-flex-vh-center pr-0">${source.json}</div>
</label>`;
const listItem = new ListItem(

View File

@@ -91,9 +91,9 @@ class UtilGenTables {
chapterOut.index = table.chapter.index;
table.chapter = chapterOut;
}
} else if (table._tmpMeta.metaType === "class") {
} else if (table._tmpMeta.metaType === "class" || table._tmpMeta.metaType === "classFluff") {
table.parentEntity = {
type: table._tmpMeta.metaType,
type: table._tmpMeta.metaType = "class",
name: table._tmpMeta.className,
source: table._tmpMeta.classSource,
};
@@ -180,17 +180,9 @@ class UtilGenTables {
this._mutCleanData(tbl);
});
stacks.tableGroup.forEach(tg => {
const cleanSections = tg.path.filter(ent => ent.name).map(ent => this._getCleanSectionName(ent.name));
if (!tg.name) throw new Error("Group had no name!");
if (!this._isSectionInTitle(cleanSections, tg.name)) {
tg.name = `${cleanSections.last()}; ${tg.name}`;
}
this._mutDataAddPage(tg);
tg.source = doc[opts.headProp].source || doc[opts.headProp].id;
this._mutCleanData(tg);
this._mutPostProcessTableGroups({
stacks,
fnGetSource: it => doc[opts.headProp].source || doc[opts.headProp].id,
});
return stacks;
@@ -223,6 +215,25 @@ class UtilGenTables {
}));
});
if (cls.fluff) {
this._doSearch({
sectionOrders,
path,
tmpMeta: {
metaType: "classFluff",
className: cls.name,
classSource: cls.source || Parser.SRC_PHB,
},
section: cls.name,
data: {entries: cls.fluff},
stacks: stacks,
isRequireIncludes: true,
// Used to deduplicate headers
name: cls.name,
});
}
stacks.table.forEach(it => {
it.name = it.caption;
it.source = it._tmpMeta.subclassSource || it._tmpMeta.classSource;
@@ -232,6 +243,11 @@ class UtilGenTables {
this._mutCleanData(it);
});
this._mutPostProcessTableGroups({
stacks,
fnGetSource: it => it._tmpMeta.subclassSource || it._tmpMeta.classSource,
});
return stacks;
}
@@ -276,6 +292,11 @@ class UtilGenTables {
this._mutCleanData(it);
});
this._mutPostProcessTableGroups({
stacks,
fnGetSource: it => it._tmpMeta.subclassSource || it._tmpMeta.classSource,
});
return stacks;
}
@@ -316,8 +337,35 @@ class UtilGenTables {
this._mutCleanData(it);
});
this._mutPostProcessTableGroups({
stacks,
fnGetSource: it => it._tmpMeta.source,
});
return stacks;
}
static _mutPostProcessTableGroups (
{
stacks,
fnGetSource,
},
) {
stacks.tableGroup.forEach(tg => {
const cleanSections = tg.path.filter(ent => ent.name).map(ent => this._getCleanSectionName(ent.name));
if (!tg.name && !cleanSections.length) throw new Error("Group had no name!");
if (!tg.name) {
tg.name = cleanSections.last();
} if (!this._isSectionInTitle(cleanSections, tg.name)) {
tg.name = `${cleanSections.last()}; ${tg.name}`;
}
this._mutDataAddPage(tg);
tg.source = fnGetSource(tg);
this._mutCleanData(tg);
});
}
}
globalThis.UtilGenTables = UtilGenTables;

View File

@@ -229,6 +229,7 @@ PropOrder._MONSTER = [
"tokenCredit",
"soundClip",
"foundryImg",
"foundryTokenScale",
"altArt",
@@ -1187,6 +1188,7 @@ PropOrder._VEHICLE = [
"foundrySystem",
"foundryFlags",
"foundryImg",
"foundryTokenScale",
];
PropOrder._VEHICLE_UPGRADE = [
"name",
@@ -1215,6 +1217,7 @@ PropOrder._RACE_FLUFF = [
];
PropOrder._ITEM = [
"name",
"alias",
"namePrefix",
"nameSuffix",
"nameRemove",
@@ -1392,6 +1395,7 @@ PropOrder._ITEM__COPY_MOD = [
];
PropOrder._MAGICVARIANT = [
"name",
"alias",
"source",
"type",
@@ -1466,6 +1470,8 @@ PropOrder._OBJECT = [
"hasFluffImages",
"fluff",
"foundryTokenScale",
];
PropOrder._OPTIONALFEATURE = [
"name",

View File

@@ -4,6 +4,7 @@ class Prx {
static addHook (prop, hook) {
this.px._hooks[prop] = this.px._hooks[prop] || [];
this.px._hooks[prop].push(hook);
return hook;
}
static addHookAll (hook) {
@@ -966,8 +967,8 @@ class ListUiUtil {
ag: "div",
clazz: "ve-hidden ve-flex",
children: [
e_({tag: "div", clazz: "col-0-5"}),
e_({tag: "div", clazz: "col-11-5 ui-list__wrp-preview py-2 pr-2"}),
e_({tag: "div", clazz: "ve-col-0-5"}),
e_({tag: "div", clazz: "ve-col-11-5 ui-list__wrp-preview py-2 pr-2"}),
],
}).appendTo(item.ele);
} else elePreviewWrp = item.ele.lastElementChild;
@@ -2905,7 +2906,7 @@ class InputUiUtil {
if (opts.autocomplete) {
// prevent double-binding the return key if we have autocomplete enabled
await MiscUtil.pDelay(17); // arbitrary delay to allow dropdown to render (~1000/60, i.e. 1 60 FPS frame)
if ($modalInner.find(`.typeahead.dropdown-menu`).is(":visible")) return;
if ($modalInner.find(`.typeahead.ve-dropdown-menu`).is(":visible")) return;
}
evt.stopPropagation();
@@ -3554,31 +3555,31 @@ class SourceUiUtil {
const $stageInitial = $$`<div class="h-100 w-100 ve-flex-vh-center"><div class="ve-flex-col">
<h3 class="ve-text-center">${isEditMode ? "Edit Homebrew Source" : "Add a Homebrew Source"}</h3>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="The name or title for the homebrew you wish to create. This could be the name of a book or PDF; for example, 'Monster Manual'">Title</span>
${$iptName}
</div></div>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="An abbreviated form of the title. This will be shown in lists on the site, and in the top-right corner of stat blocks or data entries; for example, 'MM'">Abbreviation</span>
${$iptAbv}
</div></div>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="This will be used to identify your homebrew universally, so should be unique to you and you alone">JSON Identifier</span>
${$iptJson}
</div></div>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="A color which should be used when displaying the source abbreviation">Color</span>
${$iptColor}
</div></div>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="A link to the original homebrew, e.g. a GM Binder page">Source URL</span>
${$iptUrl}
</div></div>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="A comma-separated list of authors, e.g. 'John Doe, Joe Bloggs'">Author(s)</span>
${$iptAuthors}
</div></div>
<div class="ui-source__row mb-2"><div class="col-12 ve-flex-v-center">
<div class="ui-source__row mb-2"><div class="ve-col-12 ve-flex-v-center">
<span class="mr-2 ui-source__name help" title="A comma-separated list of people who converted the homebrew to 5etools' format, e.g. 'John Doe, Joe Bloggs'">Converted By</span>
${$iptConverters}
</div></div>
@@ -3620,8 +3621,8 @@ class SourceUiUtil {
const $stageExisting = $$`<div class="h-100 w-100 ve-flex-vh-center ve-hidden"><div>
<h3 class="ve-text-center">Select a Homebrew Source</h3>
<div class="mb-2"><div class="col-12 ve-flex-vh-center">${$selExisting}</div></div>
<div class="col-12 ve-flex-vh-center">${$btnBackExisting}${$btnConfirmExisting}</div>
<div class="mb-2"><div class="ve-col-12 ve-flex-vh-center">${$selExisting}</div></div>
<div class="ve-col-12 ve-flex-vh-center">${$btnBackExisting}${$btnConfirmExisting}</div>
</div></div>`.appendTo(options.$parent);
}
}
@@ -5529,8 +5530,8 @@ class ComponentUiUtil {
});
const $ele = $$`<label class="ve-flex-v-center py-1 stripe-even">
<div class="col-1 ve-flex-vh-center">${$cb}</div>
<div class="col-11 ve-flex-v-center">${displayValue}</div>
<div class="ve-col-1 ve-flex-vh-center">${$cb}</div>
<div class="ve-col-11 ve-flex-v-center">${displayValue}</div>
</label>`;
$eles.push($ele);

View File

@@ -2,7 +2,7 @@
// in deployment, `IS_DEPLOYED = "<version number>";` should be set below.
globalThis.IS_DEPLOYED = undefined;
globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.200.0"/* 5ETOOLS_VERSION__CLOSE */;
globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.201.0"/* 5ETOOLS_VERSION__CLOSE */;
globalThis.DEPLOYED_IMG_ROOT = undefined;
// for the roll20 script to set
globalThis.IS_VTT = false;
@@ -2683,7 +2683,7 @@ globalThis.UrlUtil = {
categoryToPage (category) { return UrlUtil.CAT_TO_PAGE[category]; },
categoryToHoverPage (category) { return UrlUtil.CAT_TO_HOVER_PAGE[category] || UrlUtil.categoryToPage(category); },
pageToDisplayPage (page) { return UrlUtil.PG_TO_NAME[page] || page; },
pageToDisplayPage (page) { return UrlUtil.PG_TO_NAME[page] || (page || "").replace(/\.html$/, ""); },
getFilename (url) { return url.slice(url.lastIndexOf("/") + 1); },
@@ -3129,6 +3129,14 @@ UrlUtil.SUBLIST_PAGES = {
[UrlUtil.PG_DECKS]: true,
};
UrlUtil.FAUX_PAGES = {
[UrlUtil.PG_CLASS_SUBCLASS_FEATURES]: true,
[UrlUtil.PG_CREATURE_FEATURES]: true,
[UrlUtil.PG_VEHICLE_FEATURES]: true,
[UrlUtil.PG_OBJECT_FEATURES]: true,
[UrlUtil.PG_TRAP_FEATURES]: true,
};
UrlUtil.PAGE_TO_PROPS = {};
UrlUtil.PAGE_TO_PROPS[UrlUtil.PG_SPELLS] = ["spell"];
UrlUtil.PAGE_TO_PROPS[UrlUtil.PG_ITEMS] = ["item", "itemGroup", "itemType", "itemEntry", "itemProperty", "itemTypeAdditionalEntries", "itemMastery", "baseitem", "magicvariant"];
@@ -4252,8 +4260,9 @@ globalThis.DataUtil = {
default: throw new Error(`${msgPtFailed} Unknown variable "${m[1]}"`);
}
});
// TODO(Future) add option to format as bonus
// eslint-disable-next-line no-eval
copyTo[prop][modInfo.prop] = eval(toExec);
copyTo[prop][modInfo.prop] = eval(DataUtil.generic.variableResolver.getCleanMathExpression(toExec));
}
static _doMod_scalarAddProp ({copyTo, copyFrom, modInfo, msgPtFailed, prop}) {
@@ -4693,17 +4702,152 @@ globalThis.DataUtil = {
},
variableResolver: class {
static _getSize ({ent}) { return ent.size?.[0] || Parser.SZ_MEDIUM; }
/** @abstract */
static _ResolverBase = class {
mode;
static _SIZE_TO_MULT = {
[Parser.SZ_LARGE]: 2,
[Parser.SZ_HUGE]: 3,
[Parser.SZ_GARGANTUAN]: 4,
getResolved ({ent, msgPtFailed, detail}) {
this._doVerifyInput({ent, msgPtFailed, detail});
return this._getResolved({ent, detail});
}
_doVerifyInput ({msgPtFailed, detail}) { /* Implement as required */ }
/**
* @abstract
* @return {string}
*/
_getResolved ({ent, mode, detail}) { throw new Error("Unimplemented!"); }
getDisplayText ({msgPtFailed, detail}) {
this._doVerifyInput({msgPtFailed, detail});
return this._getDisplayText({detail});
}
/**
* @abstract
* @return {string}
*/
_getDisplayText ({detail}) { throw new Error("Unimplemented!"); }
/* -------------------------------------------- */
_getSize ({ent}) { return ent.size?.[0] || Parser.SZ_MEDIUM; }
_SIZE_TO_MULT = {
[Parser.SZ_LARGE]: 2,
[Parser.SZ_HUGE]: 3,
[Parser.SZ_GARGANTUAN]: 4,
};
_getSizeMult (size) { return this._SIZE_TO_MULT[size] ?? 1; }
};
static _getSizeMult (size) { return this._SIZE_TO_MULT[size] ?? 1; }
static _ResolverName = class extends this._ResolverBase {
mode = "name";
_getResolved ({ent, detail}) { return ent.name; }
_getDisplayText ({detail}) { return "(name)"; }
};
static _getCleanMathExpression (str) { return str.replace(/[^-+/*0-9.,]+/g, ""); }
static _ResolverShortName = class extends this._ResolverBase {
mode = "short_name";
_getResolved ({ent, detail}) { return Renderer.monster.getShortName(ent); }
_getDisplayText ({detail}) { return "(short name)"; }
};
static _ResolverTitleShortName = class extends this._ResolverBase {
mode = "title_short_name";
_getResolved ({ent, detail}) { return Renderer.monster.getShortName(ent, {isTitleCase: true}); }
_getDisplayText ({detail}) { return "(short title name)"; }
};
/** @abstract */
static _ResolverAbilityScore = class extends this._ResolverBase {
_doVerifyInput ({msgPtFailed, detail}) {
if (!Parser.ABIL_ABVS.includes(detail)) throw new Error(`${msgPtFailed ? `${msgPtFailed} ` : ""} Unknown ability score "${detail}"`);
}
};
static _ResolverDc = class extends this._ResolverAbilityScore {
mode = "dc";
_getResolved ({ent, detail}) { return 8 + Parser.getAbilityModNumber(Number(ent[detail])) + Parser.crToPb(ent.cr); }
_getDisplayText ({detail}) { return `(${detail.toUpperCase()} DC)`; }
};
static _ResolverSpellDc = class extends this._ResolverDc {
mode = "spell_dc";
_getDisplayText ({detail}) { return `(${detail.toUpperCase()} spellcasting DC)`; }
};
static _ResolverToHit = class extends this._ResolverAbilityScore {
mode = "to_hit";
_getResolved ({ent, detail}) {
const total = Parser.crToPb(ent.cr) + Parser.getAbilityModNumber(Number(ent[detail]));
return total >= 0 ? `+${total}` : total;
}
_getDisplayText ({detail}) { return `(${detail.toUpperCase()} to-hit)`; }
};
static _ResolverDamageMod = class extends this._ResolverAbilityScore {
mode = "damage_mod";
_getResolved ({ent, detail}) {
const total = Parser.getAbilityModNumber(Number(ent[detail]));
return total === 0 ? "" : total > 0 ? ` + ${total}` : ` - ${Math.abs(total)}`;
}
_getDisplayText ({detail}) { return `(${detail.toUpperCase()} damage modifier)`; }
};
static _ResolverDamageAvg = class extends this._ResolverBase {
mode = "damage_avg";
_getResolved ({ent, detail}) {
const replaced = detail
.replace(/\b(?<abil>str|dex|con|int|wis|cha)\b/gi, (...m) => Parser.getAbilityModNumber(Number(ent[m.last().abil])))
.replace(/\bsize_mult\b/g, () => this._getSizeMult(this._getSize({ent})));
// eslint-disable-next-line no-eval
return Math.floor(eval(DataUtil.generic.variableResolver.getCleanMathExpression(replaced)));
}
_getDisplayText ({detail}) { return "(damage average)"; } // TODO(Future) more specific
};
static _ResolverSizeMult = class extends this._ResolverBase {
mode = "size_mult";
_getResolved ({ent, detail}) {
const mult = this._getSizeMult(this._getSize({ent}));
if (!detail) return mult;
// eslint-disable-next-line no-eval
return Math.floor(eval(`${mult} * ${DataUtil.generic.variableResolver.getCleanMathExpression(detail)}`));
}
_getDisplayText ({detail}) { return "(size multiplier)"; } // TODO(Future) more specific
};
static _RESOLVERS = [
new this._ResolverName(),
new this._ResolverShortName(),
new this._ResolverTitleShortName(),
new this._ResolverDc(),
new this._ResolverSpellDc(),
new this._ResolverToHit(),
new this._ResolverDamageMod(),
new this._ResolverDamageAvg(),
new this._ResolverSizeMult(),
];
static _MODE_LOOKUP = (() => {
return Object.fromEntries(
this._RESOLVERS.map(resolver => [resolver.mode, resolver]),
);
})();
static _WALKER = null;
static resolve ({obj, ent, msgPtFailed = null}) {
@@ -4716,55 +4860,39 @@ globalThis.DataUtil = {
string: str => str.replace(/<\$(?<variable>[^$]+)\$>/g, (...m) => {
const [mode, detail] = m.last().variable.split("__");
switch (mode) {
case "name": return ent.name;
case "short_name":
case "title_short_name": {
return Renderer.monster.getShortName(ent, {isTitleCase: mode === "title_short_name"});
}
const resolver = this._MODE_LOOKUP[mode];
if (!resolver) return m[0];
case "dc":
case "spell_dc": {
if (!Parser.ABIL_ABVS.includes(detail)) throw new Error(`${msgPtFailed ? `${msgPtFailed} ` : ""} Unknown ability score "${detail}"`);
return 8 + Parser.getAbilityModNumber(Number(ent[detail])) + Parser.crToPb(ent.cr);
}
case "to_hit": {
if (!Parser.ABIL_ABVS.includes(detail)) throw new Error(`${msgPtFailed ? `${msgPtFailed} ` : ""} Unknown ability score "${detail}"`);
const total = Parser.crToPb(ent.cr) + Parser.getAbilityModNumber(Number(ent[detail]));
return total >= 0 ? `+${total}` : total;
}
case "damage_mod": {
if (!Parser.ABIL_ABVS.includes(detail)) throw new Error(`${msgPtFailed ? `${msgPtFailed} ` : ""} Unknown ability score "${detail}"`);
const total = Parser.getAbilityModNumber(Number(ent[detail]));
return total === 0 ? "" : total > 0 ? ` + ${total}` : ` - ${Math.abs(total)}`;
}
case "damage_avg": {
const replaced = detail
.replace(/\b(?<abil>str|dex|con|int|wis|cha)\b/gi, (...m) => Parser.getAbilityModNumber(Number(ent[m.last().abil])))
.replace(/\bsize_mult\b/g, () => this._getSizeMult(this._getSize({ent})));
// eslint-disable-next-line no-eval
return Math.floor(eval(this._getCleanMathExpression(replaced)));
}
case "size_mult": {
const mult = this._getSizeMult(this._getSize({ent}));
if (!detail) return mult;
// eslint-disable-next-line no-eval
return Math.floor(eval(`${mult} * ${this._getCleanMathExpression(detail)}`));
}
default: return m[0];
}
return resolver.getResolved({ent, msgPtFailed, detail});
}),
},
);
}
static getHumanReadable ({obj, msgPtFailed}) {
DataUtil.generic.variableResolver._WALKER ||= MiscUtil.getWalker();
return DataUtil.generic.variableResolver._WALKER
.walk(
obj,
{
string: str => this.getHumanReadableString(str, {msgPtFailed}),
},
);
}
static getHumanReadableString (str, {msgPtFailed = null} = {}) {
return str.replace(/<\$(?<variable>[^$]+)\$>/g, (...m) => {
const [mode, detail] = m.last().variable.split("__");
const resolver = this._MODE_LOOKUP[mode];
if (!resolver) return m[0];
return resolver.getDisplayText({msgPtFailed, detail});
});
}
static getCleanMathExpression (str) { return str.replace(/[^-+/*0-9.,]+/g, ""); }
},
getVersions (parent, {impl = null, isExternalApplicationIdentityOnly = false} = {}) {

View File

@@ -11,12 +11,12 @@ class VariantRulesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Name",
css: "bold col-10 pl-0",
css: "bold ve-col-10 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Type",
css: "col-3 ve-text-center pr-0",
css: "ve-col-3 ve-text-center pr-0",
colStyle: "text-center",
}),
];
@@ -81,9 +81,9 @@ class VariantRulesPage extends ListPage {
const hash = UrlUtil.autoEncodeHash(rule);
eleLi.innerHTML = `<a href="#${hash}" class="lst--border lst__row-inner">
<span class="bold col-7 pl-0">${rule.name}</span>
<span class="col-3 ve-text-center">${rule.ruleType ? Parser.ruleTypeToFull(rule.ruleType) : "\u2014"}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(rule.source)} pr-0" title="${Parser.sourceJsonToFull(rule.source)}" ${Parser.sourceJsonToStyle(rule.source)}>${source}</span>
<span class="bold ve-col-7 pl-0">${rule.name}</span>
<span class="ve-col-3 ve-text-center">${rule.ruleType ? Parser.ruleTypeToFull(rule.ruleType) : "\u2014"}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(rule.source)} pr-0" title="${Parser.sourceJsonToFull(rule.source)}" ${Parser.sourceJsonToStyle(rule.source)}>${source}</span>
</a>`;
const listItem = new ListItem(

View File

@@ -11,12 +11,12 @@ class VehiclesSublistManager extends SublistManager {
return [
new SublistCellTemplate({
name: "Type",
css: "col-8 pl-0 ve-text-center",
css: "ve-col-8 pl-0 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Name",
css: "bold col-4 pr-0",
css: "bold ve-col-4 pr-0",
colStyle: "",
}),
];
@@ -87,9 +87,9 @@ class VehiclesPage extends ListPage {
const displayType = it.vehicleType ? Parser.vehicleTypeToFull(it.vehicleType) : it.upgradeType.map(t => Parser.vehicleTypeToFull(t));
eleLi.innerHTML = `<a href="#${UrlUtil.autoEncodeHash(it)}" class="lst--border lst__row-inner">
<span class="col-6 pl-0 ve-text-center">${displayType}</span>
<span class="bold col-4">${it.name}</span>
<span class="col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
<span class="ve-col-6 pl-0 ve-text-center">${displayType}</span>
<span class="bold ve-col-4">${it.name}</span>
<span class="ve-col-2 ve-text-center ${Parser.sourceJsonToColor(it.source)} pr-0" title="${Parser.sourceJsonToFull(it.source)}" ${Parser.sourceJsonToStyle(it.source)}>${source}</span>
</a>`;
const listItem = new ListItem(