This commit is contained in:
TheGiddyLimit
2024-01-10 17:30:40 +00:00
parent e3bf75f42a
commit 5ffd4acdb4
26 changed files with 473 additions and 322 deletions

View File

@@ -11,5 +11,3 @@ version=$1
# Set the IS_DEPLOYED variable for production. # Set the IS_DEPLOYED variable for production.
sed -i 's/IS_DEPLOYED\s*=\s*undefined/IS_DEPLOYED='"\"${version}\""'/g' js/utils.js sed -i 's/IS_DEPLOYED\s*=\s*undefined/IS_DEPLOYED='"\"${version}\""'/g' js/utils.js
sed -i 's#DEPLOYED_IMG_ROOT\s*=\s*undefined#DEPLOYED_IMG_ROOT='"\"https://raw.githubusercontent.com/5etools-mirror-2/5etools-img/main/\""'#g' js/utils.js

5
.github/set-img-root.sh vendored Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -e
sed -i 's#DEPLOYED_IMG_ROOT\s*=\s*undefined#DEPLOYED_IMG_ROOT='"\"https://raw.githubusercontent.com/5etools-mirror-2/5etools-img/main/\""'#g' js/utils.js

View File

@@ -32,16 +32,17 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@master uses: actions/checkout@master
- name: Set Deployed Flag - name: Set Deployed Flag and Image Root
run: | run: |
bash ./.github/set-deployed-flag.sh ${{ github.ref_name }} bash ./.github/set-deployed-flag.sh ${{ github.ref_name }}
bash ./.github/set-img-root.sh
# Notably: remove anything which should not be added to the service worker manifest: # Notably: remove anything which should not be added to the service worker manifest:
# - `homebrew` # - `homebrew`
# - `prerelease` # - `prerelease`
- name: Cleanup - name: Cleanup
run: | run: |
rm -rf .gitmodules *.md *.zip scss spellcheck homebrew prerelease rm -rf *.md *.zip scss spellcheck homebrew prerelease
ls -lah ls -lah
# Remove entries from the `.gitignore` so the gh-pages action can correctly add+commit them to the pages branch # Remove entries from the `.gitignore` so the gh-pages action can correctly add+commit them to the pages branch

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "img"]
path = img
url = ssh://git@git.develop.5etools.com:2222/5etools/5etools_img.git
branch = master

View File

@@ -99,6 +99,7 @@
<div class="split-v-center conv__out_control__wrp"> <div class="split-v-center conv__out_control__wrp">
<button id="editable" class="btn btn-sm btn-default">Enable Editing</button> <button id="editable" class="btn btn-sm btn-default">Enable Editing</button>
<div class="ve-flex-v-center"> <div class="ve-flex-v-center">
<button id="preview" class="btn btn-sm btn-default mr-2">Preview</button>
<button id="save_local" class="btn btn-sm btn-default hidden mr-2">Save to Homebrew</button> <button id="save_local" class="btn btn-sm btn-default hidden mr-2">Save to Homebrew</button>
<div class="btn-group ve-flex-vh-center"> <div class="btn-group ve-flex-vh-center">

File diff suppressed because one or more lines are too long

View File

@@ -748,7 +748,9 @@
"note": "from nonmagical attacks that aren't adamantine", "note": "from nonmagical attacks that aren't adamantine",
"cond": true "cond": true
} }
] ],
"tokenCredit": "Jeferson Cordeiro",
"hasToken": true
}, },
{ {
"name": "Asmodeus", "name": "Asmodeus",
@@ -3136,7 +3138,9 @@
"fire", "fire",
"lightning", "lightning",
"slashing" "slashing"
] ],
"tokenCredit": "Unknown",
"hasToken": true
}, },
{ {
"name": "Devorastus", "name": "Devorastus",
@@ -4493,7 +4497,9 @@
"note": "from nonmagical attacks that aren't adamantine", "note": "from nonmagical attacks that aren't adamantine",
"cond": true "cond": true
} }
] ],
"tokenCredit": "Artguyonthefly",
"hasToken": true
}, },
{ {
"name": "Glasya", "name": "Glasya",
@@ -4908,7 +4914,9 @@
"speed": { "speed": {
"walk": 0 "walk": 0
}, },
"spellcasting": null "spellcasting": null,
"tokenCredit": "Irene Meniconi",
"hasToken": true
}, },
{ {
"name": "Halog", "name": "Halog",
@@ -5171,7 +5179,9 @@
}, },
"immune": [ "immune": [
"cold" "cold"
] ],
"tokenCredit": "Caverns of the Snow Witch",
"hasToken": true
}, },
{ {
"name": "Jenevere", "name": "Jenevere",
@@ -5495,12 +5505,14 @@
] ]
} }
}, },
"tokenCredit": "CrazyMind351",
"damageTags": [ "damageTags": [
"B", "B",
"L", "L",
"P", "P",
"R" "R"
] ],
"hasToken": true
}, },
{ {
"name": "Lesser Tyrant Shadow", "name": "Lesser Tyrant Shadow",
@@ -7131,7 +7143,9 @@
], ],
"int": 16, "int": 16,
"wis": 16, "wis": 16,
"cha": 16 "cha": 16,
"tokenCredit": "Konstantin Vavilov",
"hasToken": true
}, },
{ {
"name": "Rimmon", "name": "Rimmon",

View File

@@ -2624,6 +2624,11 @@
"date": "2024-01-06", "date": "2024-01-06",
"txt": "- Added Dynamic Map Viewer support to Dungeons of Drakkenheim\n- Added \"Legacy\" Miscellaneous filter to various list pages\n- Added \"Legacy\" marker to Omnisearch results\n- (Fixed typos/added tags)" "txt": "- Added Dynamic Map Viewer support to Dungeons of Drakkenheim\n- Added \"Legacy\" Miscellaneous filter to various list pages\n- Added \"Legacy\" marker to Omnisearch results\n- (Fixed typos/added tags)"
}, },
{
"ver": "1.197.4",
"date": "2024-01-10",
"txt": "- Added \"One D&D\" filter to Prerelease Content Manager \"Get Prerelease Content\" view\n- Switched \"Added [date]\" column to \"Published [date]\" in Prerelease Content Manager \"Get Prerelease Content\" view\n- Added tokens for Chains of Asmodeus NPCs/other creatures\n- (Brew) Added \"Preview\" button to text converter output\n- (Brew) Added support for \"month\" spell durations\n- (Brew) Fixed homebrew content which depends on prerelease content failing to load that prerelease content\n- (Fixed typos/added tags)"
},
{ {
"ver": "1.198.0", "ver": "1.198.0",
"date": "2024-01-06", "date": "2024-01-06",

View File

@@ -136,7 +136,15 @@ class BaseParserFeature extends BaseParser {
} }
handleStack(); handleStack();
if (pres.length) state.entity.prerequisite = pres; if (!pres.length) return;
const presDeduped = [];
pres.forEach(pre => {
if (presDeduped.some(it => CollectionUtil.deepEquals(pre, it))) return;
presDeduped.push(pre);
});
state.entity.prerequisite = presDeduped;
} }
} }

View File

@@ -337,6 +337,8 @@ class SpellParser extends BaseParser {
unit = unit.toLowerCase().trim(); unit = unit.toLowerCase().trim();
switch (unit) { switch (unit) {
case "days": case "days":
case "weeks":
case "months":
case "years": case "years":
case "hours": case "hours":
case "minutes": case "minutes":
@@ -344,6 +346,8 @@ class SpellParser extends BaseParser {
case "rounds": return unit.slice(0, -1); case "rounds": return unit.slice(0, -1);
case "day": case "day":
case "week":
case "month":
case "year": case "year":
case "hour": case "hour":
case "minute": case "minute":
@@ -441,7 +445,7 @@ class SpellParser extends BaseParser {
if (dur.toLowerCase() === "special") return stats.duration = [{type: "special"}]; if (dur.toLowerCase() === "special") return stats.duration = [{type: "special"}];
if (dur.toLowerCase() === "permanent") return stats.duration = [{type: "permanent"}]; if (dur.toLowerCase() === "permanent") return stats.duration = [{type: "permanent"}];
const mConcOrUpTo = /^(concentration, )?up to (\d+|an?) (hour|minute|turn|round|week|day|year)(?:s)?$/i.exec(dur); const mConcOrUpTo = /^(concentration, )?up to (\d+|an?) (hour|minute|turn|round|week|month|day|year)(?:s)?$/i.exec(dur);
if (mConcOrUpTo) { if (mConcOrUpTo) {
const amount = mConcOrUpTo[2].toLowerCase().startsWith("a") ? 1 : Number(mConcOrUpTo[2]); const amount = mConcOrUpTo[2].toLowerCase().startsWith("a") ? 1 : Number(mConcOrUpTo[2]);
const out = {type: "timed", duration: {type: this._getCleanTimeUnit(mConcOrUpTo[3], true, options), amount}, concentration: true}; const out = {type: "timed", duration: {type: this._getCleanTimeUnit(mConcOrUpTo[3], true, options), amount}, concentration: true};
@@ -450,7 +454,7 @@ class SpellParser extends BaseParser {
return stats.duration = [out]; return stats.duration = [out];
} }
const mTimed = /^(\d+) (hour|minute|turn|round|week|day|year)(?:s)?$/i.exec(dur); const mTimed = /^(\d+) (hour|minute|turn|round|week|month|day|year)(?:s)?$/i.exec(dur);
if (mTimed) return stats.duration = [{type: "timed", duration: {type: this._getCleanTimeUnit(mTimed[2], true, options), amount: Number(mTimed[1])}}]; if (mTimed) return stats.duration = [{type: "timed", duration: {type: this._getCleanTimeUnit(mTimed[2], true, options), amount: Number(mTimed[1])}}];
const mDispelledTriggered = /^until dispelled( or triggered)?$/i.exec(dur); const mDispelledTriggered = /^until dispelled( or triggered)?$/i.exec(dur);

View File

@@ -996,21 +996,50 @@ class ConverterUi extends BaseComponent {
JqueryUtil.doToast({type: "warning", content: "Enabled editing. Note that edits will be overwritten as you parse new stat blocks."}); JqueryUtil.doToast({type: "warning", content: "Enabled editing. Note that edits will be overwritten as you parse new stat blocks."});
}); });
const $btnSaveLocal = $(`#save_local`).click(async () => { $(`#preview`)
const output = this._outText; .on("click", async evt => {
const metaCurr = this._getCurrentEntities();
if (!(output || "").trim()) { if (!metaCurr?.entities?.length) return JqueryUtil.doToast({content: "Nothing to preview!", type: "warning"});
return JqueryUtil.doToast({ if (metaCurr.error) return JqueryUtil.doToast({content: `Current output was not valid JSON!`, type: "danger"});
content: "Nothing to save!",
type: "warning", const entries = !this.activeConverter.prop
? metaCurr.entities.flat()
: metaCurr.entities
.map(ent => {
// Handle nameless/sourceless entities (e.g. tables)
if (!ent.name) ent.name = "(Unnamed)";
if (!ent.source) ent.source = VeCt.STR_GENERIC;
return {
type: "statblockInline",
dataType: this.activeConverter.prop,
data: ent,
};
}); });
}
try { Renderer.hover.getShowWindow(
Renderer.hover.$getHoverContent_generic({
type: "entries",
entries,
}),
Renderer.hover.getWindowPositionFromEvent(evt),
{
title: "Preview",
isPermanent: true,
},
);
});
const $btnSaveLocal = $(`#save_local`).click(async () => {
const metaCurr = this._getCurrentEntities();
if (!metaCurr?.entities?.length) return JqueryUtil.doToast({content: "Nothing to save!", type: "warning"});
if (metaCurr.error) return JqueryUtil.doToast({content: `Current output was not valid JSON!`, type: "danger"});
const prop = this.activeConverter.prop; const prop = this.activeConverter.prop;
const entries = JSON.parse(`[${output}]`);
const invalidSources = entries.map(it => !it.source || !BrewUtil2.hasSourceJson(it.source) ? (it.name || it.caption || "(Unnamed)").trim() : false).filter(Boolean); const invalidSources = metaCurr.entities.map(it => !it.source || !BrewUtil2.hasSourceJson(it.source) ? (it.name || it.caption || "(Unnamed)").trim() : false).filter(Boolean);
if (invalidSources.length) { if (invalidSources.length) {
JqueryUtil.doToast({ JqueryUtil.doToast({
content: `One or more entries have missing or unknown sources: ${invalidSources.join(", ")}`, content: `One or more entries have missing or unknown sources: ${invalidSources.join(", ")}`,
@@ -1020,7 +1049,7 @@ class ConverterUi extends BaseComponent {
} }
const brewDocEditable = await BrewUtil2.pGetEditableBrewDoc(); const brewDocEditable = await BrewUtil2.pGetEditableBrewDoc();
const uneditableSources = entries const uneditableSources = metaCurr.entities
.filter(ent => !(brewDocEditable?.body?._meta?.sources || []).some(src => src.json === ent.source)) .filter(ent => !(brewDocEditable?.body?._meta?.sources || []).some(src => src.json === ent.source))
.map(ent => ent.source); .map(ent => ent.source);
if (uneditableSources.length) { if (uneditableSources.length) {
@@ -1034,7 +1063,7 @@ class ConverterUi extends BaseComponent {
// ignore duplicates // ignore duplicates
const _dupes = {}; const _dupes = {};
const dupes = []; const dupes = [];
const dedupedEntries = entries const dedupedEntries = metaCurr.entities
.map(it => { .map(it => {
const lSource = it.source.toLowerCase(); const lSource = it.source.toLowerCase();
const lName = it.name.toLowerCase(); const lName = it.name.toLowerCase();
@@ -1100,13 +1129,6 @@ class ConverterUi extends BaseComponent {
type: "success", type: "success",
content: `Saved!`, content: `Saved!`,
}); });
} catch (e) {
JqueryUtil.doToast({
content: `Current output was not valid JSON!`,
type: "danger",
});
setTimeout(() => { throw e; });
}
}); });
const hkConverter = () => { const hkConverter = () => {
$btnSaveLocal.toggleClass("hidden", !this.activeConverter.canSaveLocal); $btnSaveLocal.toggleClass("hidden", !this.activeConverter.canSaveLocal);
@@ -1115,26 +1137,20 @@ class ConverterUi extends BaseComponent {
hkConverter(); hkConverter();
$(`#btn-output-download`).click(() => { $(`#btn-output-download`).click(() => {
const output = this._outText; const metaCurr = this._getCurrentEntities();
if (!output || !output.trim()) {
return JqueryUtil.doToast({
content: "Nothing to download!",
type: "danger",
});
}
try { if (!metaCurr?.entities?.length) return JqueryUtil.doToast({content: "Nothing to download!", type: "warning"});
const prop = this.activeConverter.prop; if (metaCurr.error) {
const out = {[prop]: JSON.parse(`[${output}]`)};
DataUtil.userDownload(`converter-output`, out);
} catch (e) {
JqueryUtil.doToast({ JqueryUtil.doToast({
content: `Current output was not valid JSON. Downloading as <span class="code">.txt</span> instead.`, content: `Current output was not valid JSON. Downloading as <span class="code">.txt</span> instead.`,
type: "warning", type: "warning",
}); });
DataUtil.userDownloadText(`converter-output.txt`, output); DataUtil.userDownloadText(`converter-output.txt`, metaCurr.text);
setTimeout(() => { throw e; }); return;
} }
const out = {[this.activeConverter.prop]: metaCurr.entities};
DataUtil.userDownload(`converter-output`, out);
}); });
$(`#btn-output-copy`).click(async evt => { $(`#btn-output-copy`).click(async evt => {
@@ -1204,6 +1220,18 @@ class ConverterUi extends BaseComponent {
window.dispatchEvent(new Event("toolsLoaded")); window.dispatchEvent(new Event("toolsLoaded"));
} }
_getCurrentEntities () {
const output = this._outText;
if (!(output || "").trim()) return null;
try {
return {entities: JSON.parse(`[${output}]`)};
} catch (e) {
return {error: e.message, text: output.trim()};
}
}
initSideMenu () { initSideMenu () {
const $mnu = $(`.sidemenu`); const $mnu = $(`.sidemenu`);
@@ -1267,7 +1295,7 @@ class ConverterUi extends BaseComponent {
if (append) { if (append) {
const strs = [asCleanString, this._outText]; const strs = [asCleanString, this._outText];
if (this._state.appendPrependMode === "prepend") strs.reverse(); if (this._state.appendPrependMode === "prepend") strs.reverse();
this._outText = strs.join(",\n"); this._outText = strs.map(it => it.trimEnd()).join(",\n");
this._state.hasAppended = true; this._state.hasAppended = true;
} else { } else {
this._outText = asCleanString; this._outText = asCleanString;

View File

@@ -1368,6 +1368,7 @@ class SpellcastingTraitConvert {
{re: /\/rest/i, prop: "rest"}, {re: /\/rest/i, prop: "rest"},
{re: /\/day/i, prop: "daily"}, {re: /\/day/i, prop: "daily"},
{re: /\/week/i, prop: "weekly"}, {re: /\/week/i, prop: "weekly"},
{re: /\/month/i, prop: "monthly"},
{re: /\/yeark/i, prop: "yearly"}, {re: /\/yeark/i, prop: "yearly"},
]; ];

View File

@@ -191,8 +191,9 @@ class PageFilterSpells extends PageFilter {
return "24+ Hours"; return "24+ Hours";
} }
case "week":
case "day": case "day":
case "week":
case "month":
case "year": return "24+ Hours"; case "year": return "24+ Hours";
default: return "Special"; default: return "Special";
} }

View File

@@ -2352,6 +2352,11 @@ class CreatureBuilder extends Builder {
type: "weekly", type: "weekly",
mode: "frequency", mode: "frequency",
}, },
{
display: "\uD835\uDC65/month (/each) spells",
type: "monthly",
mode: "frequency",
},
{ {
display: "\uD835\uDC65/year (/each) spells", display: "\uD835\uDC65/year (/each) spells",
type: "yearly", type: "yearly",
@@ -2439,6 +2444,7 @@ class CreatureBuilder extends Builder {
if (trait.daily) handleFrequency("daily"); if (trait.daily) handleFrequency("daily");
if (trait.rest) handleFrequency("rest"); if (trait.rest) handleFrequency("rest");
if (trait.weekly) handleFrequency("weekly"); if (trait.weekly) handleFrequency("weekly");
if (trait.monthly) handleFrequency("monthly");
if (trait.yearly) handleFrequency("yearly"); if (trait.yearly) handleFrequency("yearly");
if (trait.spells) { if (trait.spells) {
Object.entries(trait.spells).forEach(([k, v]) => { Object.entries(trait.spells).forEach(([k, v]) => {
@@ -2539,6 +2545,7 @@ class CreatureBuilder extends Builder {
case "daily": return "/Day"; case "daily": return "/Day";
case "rest": return "/Rest"; case "rest": return "/Rest";
case "weekly": return "/Week"; case "weekly": return "/Week";
case "monthly": return "/Month";
case "yearly": return "/Year"; case "yearly": return "/Year";
} }
})(); })();

View File

@@ -667,8 +667,8 @@ Parser.sourceJsonToStylePart = function (source) {
Parser.sourceJsonToMarkerHtml = function (source, {isList = true, additionalStyles = ""} = {}) { Parser.sourceJsonToMarkerHtml = function (source, {isList = true, additionalStyles = ""} = {}) {
source = Parser._getSourceStringFromSource(source); source = Parser._getSourceStringFromSource(source);
// TODO(Future) consider enabling this // TODO(Future) consider enabling this
// if (SourceUtil.isPartneredSourceWotc(source)) return `<span class="help-subtle ve-source-marker ${isList ? `ve-source-marker--list` : ""} ve-source-marker--partnered ${additionalStyles}" title="D&amp;D Partnered Source"></span>`; // if (SourceUtil.isPartneredSourceWotc(source)) return `<span class="help-subtle ve-source-marker ${isList ? `ve-source-marker--list` : ""} ve-source-marker--partnered ${additionalStyles}" title="D&amp;D Partnered Source">${isList ? "" : "["}✦${isList ? "" : "]"}</span>`;
if (SourceUtil.isLegacySourceWotc(source)) return `<span class="help-subtle ve-source-marker ${isList ? `ve-source-marker--list` : ""} ve-source-marker--legacy ${additionalStyles}" title="Legacy Source">ʟ</span>`; if (SourceUtil.isLegacySourceWotc(source)) return `<span class="help-subtle ve-source-marker ${isList ? `ve-source-marker--list` : ""} ve-source-marker--legacy ${additionalStyles}" title="Legacy Source">${isList ? "" : "["}ʟ${isList ? "" : "]"}</span>`;
return ""; return "";
}; };
@@ -1345,6 +1345,7 @@ Parser.DURATION_AMOUNT_TYPES = [
"hour", "hour",
"day", "day",
"week", "week",
"month",
"year", "year",
]; ];

View File

@@ -1035,7 +1035,7 @@ globalThis.Renderer = function () {
const hidden = new Set(entry.hidden || []); const hidden = new Set(entry.hidden || []);
const toRender = [{type: "entries", name: entry.name, entries: entry.headerEntries ? MiscUtil.copyFast(entry.headerEntries) : []}]; const toRender = [{type: "entries", name: entry.name, entries: entry.headerEntries ? MiscUtil.copyFast(entry.headerEntries) : []}];
if (entry.constant || entry.will || entry.recharge || entry.charges || entry.rest || entry.daily || entry.weekly || entry.yearly || entry.ritual) { if (entry.constant || entry.will || entry.recharge || entry.charges || entry.rest || entry.daily || entry.weekly || entry.monthly || entry.yearly || entry.ritual) {
const tempList = {type: "list", style: "list-hang-notitle", items: [], data: {isSpellList: true}}; const tempList = {type: "list", style: "list-hang-notitle", items: [], data: {isSpellList: true}};
if (entry.constant && !hidden.has("constant")) tempList.items.push({type: "itemSpell", name: `Constant:`, entry: this._renderSpellcasting_getRenderableList(entry.constant).join(", ")}); if (entry.constant && !hidden.has("constant")) tempList.items.push({type: "itemSpell", name: `Constant:`, entry: this._renderSpellcasting_getRenderableList(entry.constant).join(", ")});
if (entry.will && !hidden.has("will")) tempList.items.push({type: "itemSpell", name: `At will:`, entry: this._renderSpellcasting_getRenderableList(entry.will).join(", ")}); if (entry.will && !hidden.has("will")) tempList.items.push({type: "itemSpell", name: `At will:`, entry: this._renderSpellcasting_getRenderableList(entry.will).join(", ")});
@@ -1045,6 +1045,7 @@ globalThis.Renderer = function () {
this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "rest", durationText: "/rest"}); this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "rest", durationText: "/rest"});
this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "daily", durationText: "/day"}); this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "daily", durationText: "/day"});
this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "weekly", durationText: "/week"}); this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "weekly", durationText: "/week"});
this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "monthly", durationText: "/month"});
this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "yearly", durationText: "/year"}); this._renderSpellcasting_getEntries_procPerDuration({entry, tempList, hidden, prop: "yearly", durationText: "/year"});
if (entry.ritual && !hidden.has("ritual")) tempList.items.push({type: "itemSpell", name: `Rituals:`, entry: this._renderSpellcasting_getRenderableList(entry.ritual).join(", ")}); if (entry.ritual && !hidden.has("ritual")) tempList.items.push({type: "itemSpell", name: `Rituals:`, entry: this._renderSpellcasting_getRenderableList(entry.ritual).join(", ")});
@@ -1465,8 +1466,7 @@ globalThis.Renderer = function () {
}; };
this._renderHomebrew = function (entry, textStack, meta, options) { this._renderHomebrew = function (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options); textStack[0] += `<div class="rd-homebrew__b"><div class="rd-homebrew__wrp-notice"><span class="rd-homebrew__disp-notice"></span>`;
textStack[0] += `<div class="homebrew-section"><div class="homebrew-float"><span class="homebrew-notice"></span>`;
if (entry.oldEntries) { if (entry.oldEntries) {
const hoverMeta = Renderer.hover.getInlineHover({type: "entries", name: "Homebrew", entries: entry.oldEntries}); const hoverMeta = Renderer.hover.getInlineHover({type: "entries", name: "Homebrew", entries: entry.oldEntries});
@@ -1478,7 +1478,7 @@ globalThis.Renderer = function () {
} else { } else {
markerText = "(See removed content)"; markerText = "(See removed content)";
} }
textStack[0] += `<span class="homebrew-old-content" href="#${window.location.hash}" ${hoverMeta.html}>${markerText}</span>`; textStack[0] += `<span class="rd-homebrew__disp-old-content" href="#${window.location.hash}" ${hoverMeta.html}>${markerText}</span>`;
} }
textStack[0] += `</div>`; textStack[0] += `</div>`;
@@ -1493,7 +1493,6 @@ globalThis.Renderer = function () {
} }
textStack[0] += `</div>`; textStack[0] += `</div>`;
this._renderSuffix(entry, textStack, meta, options);
}; };
this._renderCode = function (entry, textStack, meta, options) { this._renderCode = function (entry, textStack, meta, options) {
@@ -1849,7 +1848,7 @@ globalThis.Renderer = function () {
name: "Homebrew Modifications", name: "Homebrew Modifications",
entries: tooltipEntries, entries: tooltipEntries,
}); });
textStack[0] += `<span class="homebrew-inline" ${hoverMeta.html}>`; textStack[0] += `<span class="rd-homebrew__disp-inline" ${hoverMeta.html}>`;
this._recursiveRender(newText || "[...]", textStack, meta); this._recursiveRender(newText || "[...]", textStack, meta);
textStack[0] += `</span>`; textStack[0] += `</span>`;

View File

@@ -274,6 +274,7 @@ class _BrewUtil2Base {
DISPLAY_NAME_PLURAL; DISPLAY_NAME_PLURAL;
DEFAULT_AUTHOR; DEFAULT_AUTHOR;
STYLE_BTN; STYLE_BTN;
IS_PREFER_DATE_ADDED;
_LOCK = new VeLock({name: this.constructor.name}); _LOCK = new VeLock({name: this.constructor.name});
@@ -290,6 +291,15 @@ class _BrewUtil2Base {
_storage = StorageUtil; _storage = StorageUtil;
_parent = null;
/**
* @param {?_BrewUtil2Base} parent
*/
constructor ({parent = null} = {}) {
this._parent = parent;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_pActiveInit = null; _pActiveInit = null;
@@ -572,8 +582,12 @@ class _BrewUtil2Base {
}; };
} }
/* -------------------------------------------- */
getCacheIteration () { return this._cache_iteration; } getCacheIteration () { return this._cache_iteration; }
/* -------------------------------------------- */
async pSetBrew (val, {lockToken} = {}) { async pSetBrew (val, {lockToken} = {}) {
try { try {
await this._LOCK.pLock({token: lockToken}); await this._LOCK.pLock({token: lockToken});
@@ -602,6 +616,8 @@ class _BrewUtil2Base {
this._setBrewMetas(val.map(brew => this._getBrewDocReduced(brew))); this._setBrewMetas(val.map(brew => this._getBrewDocReduced(brew)));
} }
/* -------------------------------------------- */
_getBrewId (brew) { _getBrewId (brew) {
if (brew.head.url) return brew.head.url; if (brew.head.url) return brew.head.url;
if (brew.body._meta?.sources?.length) return brew.body._meta.sources.map(src => (src.json || "").toLowerCase()).sort(SortUtil.ascSortLower).join(" :: "); if (brew.body._meta?.sources?.length) return brew.body._meta.sources.map(src => (src.json || "").toLowerCase()).sort(SortUtil.ascSortLower).join(" :: ");
@@ -618,6 +634,24 @@ class _BrewUtil2Base {
return [...brews, ...brewsToAdd]; return [...brews, ...brewsToAdd];
} }
/* -------------------------------------------- */
async _pLoadParentDependencies ({unavailableSources}) {
if (!unavailableSources?.length) return false;
if (!this._parent) return false;
await Promise.allSettled(unavailableSources.map(async source => {
const url = await this._parent.pGetSourceUrl(source);
if (!url) return;
await this._parent.pAddBrewFromUrl(url, {isLazy: true});
}));
await this._parent.pAddBrewsLazyFinalize();
return false;
}
/* -------------------------------------------- */
async _pGetBrewDependencies ({brewDocs, brewsRaw = null, brewsRawLocal = null, lockToken}) { async _pGetBrewDependencies ({brewDocs, brewsRaw = null, brewsRawLocal = null, lockToken}) {
try { try {
lockToken = await this._LOCK.pLock({token: lockToken}); lockToken = await this._LOCK.pLock({token: lockToken});
@@ -629,11 +663,13 @@ class _BrewUtil2Base {
async _pGetBrewDependencies_ ({brewDocs, brewsRaw = null, brewsRawLocal = null, lockToken}) { async _pGetBrewDependencies_ ({brewDocs, brewsRaw = null, brewsRawLocal = null, lockToken}) {
const urlRoot = await this.pGetCustomUrl(); const urlRoot = await this.pGetCustomUrl();
const brewIndex = await this._pGetSourceIndex(urlRoot); const brewIndex = await this.pGetSourceIndex(urlRoot);
const toLoadSources = []; const toLoadSources = [];
const loadedSources = new Set(); const loadedSources = new Set();
const out = [];
const unavailableSources = new Set();
const brewDocsDependencies = [];
brewsRaw = brewsRaw || await this._pGetBrewRaw({lockToken}); brewsRaw = brewsRaw || await this._pGetBrewRaw({lockToken});
brewsRawLocal = brewsRawLocal || await this._pGetBrew_pGetLocalBrew({lockToken}); brewsRawLocal = brewsRawLocal || await this._pGetBrew_pGetLocalBrew({lockToken});
@@ -644,7 +680,11 @@ class _BrewUtil2Base {
brewsRaw.forEach(brew => trackLoaded(brew)); brewsRaw.forEach(brew => trackLoaded(brew));
brewsRawLocal.forEach(brew => trackLoaded(brew)); brewsRawLocal.forEach(brew => trackLoaded(brew));
brewDocs.forEach(brewDoc => toLoadSources.push(...this._getBrewDependencySources({brewDoc, brewIndex}))); brewDocs.forEach(brewDoc => {
const {available, unavailable} = this._getBrewDependencySources({brewDoc, brewIndex});
toLoadSources.push(...available);
unavailable.forEach(src => unavailableSources.add(src));
});
while (toLoadSources.length) { while (toLoadSources.length) {
const src = toLoadSources.pop(); const src = toLoadSources.pop();
@@ -653,18 +693,23 @@ class _BrewUtil2Base {
const url = this.getFileUrl(brewIndex[src], urlRoot); const url = this.getFileUrl(brewIndex[src], urlRoot);
const brewDocDep = await this._pGetBrewDocFromUrl({url}); const brewDocDep = await this._pGetBrewDocFromUrl({url});
out.push(brewDocDep); brewDocsDependencies.push(brewDocDep);
trackLoaded(brewDocDep); trackLoaded(brewDocDep);
toLoadSources.push(...this._getBrewDependencySources({brewDoc: brewDocDep, brewIndex})); const {available, unavailable} = this._getBrewDependencySources({brewDoc: brewDocDep, brewIndex});
toLoadSources.push(...available);
unavailable.forEach(src => unavailableSources.add(src));
} }
return out; return {
brewDocsDependencies,
unavailableSources: [...unavailableSources].sort(SortUtil.ascSortLower),
};
} }
async pGetSourceUrl (source) { async pGetSourceUrl (source) {
const urlRoot = await this.pGetCustomUrl(); const urlRoot = await this.pGetCustomUrl();
const brewIndex = await this._pGetSourceIndex(urlRoot); const brewIndex = await this.pGetSourceIndex(urlRoot);
if (brewIndex[source]) return this.getFileUrl(brewIndex[source], urlRoot); if (brewIndex[source]) return this.getFileUrl(brewIndex[source], urlRoot);
@@ -677,7 +722,7 @@ class _BrewUtil2Base {
} }
/** @abstract */ /** @abstract */
async _pGetSourceIndex (urlRoot) { throw new Error("Unimplemented!"); } async pGetSourceIndex (urlRoot) { throw new Error("Unimplemented!"); }
/** @abstract */ /** @abstract */
getFileUrl (path, urlRoot) { throw new Error("Unimplemented!"); } getFileUrl (path, urlRoot) { throw new Error("Unimplemented!"); }
/** @abstract */ /** @abstract */
@@ -691,15 +736,14 @@ class _BrewUtil2Base {
_PROPS_DEPS_DEEP = ["otherSources"]; _PROPS_DEPS_DEEP = ["otherSources"];
_getBrewDependencySources ({brewDoc, brewIndex}) { _getBrewDependencySources ({brewDoc, brewIndex}) {
const out = new Set(); const sources = new Set();
this._PROPS_DEPS.forEach(prop => { this._PROPS_DEPS.forEach(prop => {
const obj = brewDoc.body._meta?.[prop]; const obj = brewDoc.body._meta?.[prop];
if (!obj || !Object.keys(obj).length) return; if (!obj || !Object.keys(obj).length) return;
Object.values(obj) Object.values(obj)
.flat() .flat()
.filter(src => brewIndex[src]) .forEach(src => sources.add(src));
.forEach(src => out.add(src));
}); });
this._PROPS_DEPS_DEEP.forEach(prop => { this._PROPS_DEPS_DEEP.forEach(prop => {
@@ -708,23 +752,30 @@ class _BrewUtil2Base {
return Object.values(obj) return Object.values(obj)
.map(objSub => Object.keys(objSub)) .map(objSub => Object.keys(objSub))
.flat() .flat()
.filter(src => brewIndex[src]) .forEach(src => sources.add(src));
.forEach(src => out.add(src));
}); });
return out; const [available, unavailable] = [...sources]
.segregate(src => brewIndex[src]);
return {available, unavailable};
} }
async pAddBrewFromUrl (url, {lockToken, isLazy} = {}) { async pAddBrewFromUrl (url, {isLazy} = {}) {
let brewDocs = []; let unavailableSources = [];
try { try {
return (await this._pAddBrewFromUrl({url, lockToken, isLazy})); ({brewDocs, unavailableSources} = await this._pAddBrewFromUrl({url, isLazy}));
} catch (e) { } catch (e) {
JqueryUtil.doToast({type: "danger", content: `Failed to load ${this.DISPLAY_NAME} from URL "${url}"! ${VeCt.STR_SEE_CONSOLE}`}); JqueryUtil.doToast({type: "danger", content: `Failed to load ${this.DISPLAY_NAME} from URL "${url}"! ${VeCt.STR_SEE_CONSOLE}`});
setTimeout(() => { throw e; }); setTimeout(() => { throw e; });
}
return []; return [];
} }
await this._pLoadParentDependencies({unavailableSources});
return brewDocs;
}
async _pGetBrewDocFromUrl ({url}) { async _pGetBrewDocFromUrl ({url}) {
const json = await DataUtil.loadRawJSON(url); const json = await DataUtil.loadRawJSON(url);
return this._getBrewDoc({json, url, filename: UrlUtil.getFilename(url)}); return this._getBrewDoc({json, url, filename: UrlUtil.getFilename(url)});
@@ -741,16 +792,17 @@ class _BrewUtil2Base {
this._LOCK.unlock(); this._LOCK.unlock();
} }
return [brewDoc]; return {brewDocs: [brewDoc], unavailableSources: []};
} }
const brewDocs = [brewDoc]; const brewDocs = [brewDoc]; const unavailableSources = [];
try { try {
lockToken = await this._LOCK.pLock({token: lockToken}); lockToken = await this._LOCK.pLock({token: lockToken});
const brews = MiscUtil.copyFast(await this._pGetBrewRaw({lockToken})); const brews = MiscUtil.copyFast(await this._pGetBrewRaw({lockToken}));
const brewDocsDependencies = await this._pGetBrewDependencies({brewDocs, brewsRaw: brews, lockToken}); const {brewDocsDependencies, unavailableSources: unavailableSources_} = await this._pGetBrewDependencies({brewDocs, brewsRaw: brews, lockToken});
brewDocs.push(...brewDocsDependencies); brewDocs.push(...brewDocsDependencies);
unavailableSources.push(...unavailableSources_);
const brewsNxt = this._getNextBrews(brews, brewDocs); const brewsNxt = this._getNextBrews(brews, brewDocs);
await this.pSetBrew(brewsNxt, {lockToken}); await this.pSetBrew(brewsNxt, {lockToken});
@@ -758,20 +810,25 @@ class _BrewUtil2Base {
this._LOCK.unlock(); this._LOCK.unlock();
} }
return brewDocs; return {brewDocs, unavailableSources};
} }
async pAddBrewsFromFiles (files) { async pAddBrewsFromFiles (files) {
let brewDocs = []; let unavailableSources = [];
try { try {
const lockToken = await this._LOCK.pLock(); const lockToken = await this._LOCK.pLock();
return (await this._pAddBrewsFromFiles({files, lockToken})); ({brewDocs, unavailableSources} = await this._pAddBrewsFromFiles({files, lockToken}));
} catch (e) { } catch (e) {
JqueryUtil.doToast({type: "danger", content: `Failed to load ${this.DISPLAY_NAME} from file(s)! ${VeCt.STR_SEE_CONSOLE}`}); JqueryUtil.doToast({type: "danger", content: `Failed to load ${this.DISPLAY_NAME} from file(s)! ${VeCt.STR_SEE_CONSOLE}`});
setTimeout(() => { throw e; }); setTimeout(() => { throw e; });
return [];
} finally { } finally {
this._LOCK.unlock(); this._LOCK.unlock();
} }
return [];
await this._pLoadParentDependencies({unavailableSources});
return brewDocs;
} }
async _pAddBrewsFromFiles ({files, lockToken}) { async _pAddBrewsFromFiles ({files, lockToken}) {
@@ -779,32 +836,41 @@ class _BrewUtil2Base {
const brews = MiscUtil.copyFast(await this._pGetBrewRaw({lockToken})); const brews = MiscUtil.copyFast(await this._pGetBrewRaw({lockToken}));
const brewDocsDependencies = await this._pGetBrewDependencies({brewDocs, brewsRaw: brews, lockToken}); const {brewDocsDependencies, unavailableSources} = await this._pGetBrewDependencies({brewDocs, brewsRaw: brews, lockToken});
brewDocs.push(...brewDocsDependencies); brewDocs.push(...brewDocsDependencies);
const brewsNxt = this._getNextBrews(brews, brewDocs); const brewsNxt = this._getNextBrews(brews, brewDocs);
await this.pSetBrew(brewsNxt, {lockToken}); await this.pSetBrew(brewsNxt, {lockToken});
return brewDocs; return {brewDocs, unavailableSources};
} }
async pAddBrewsLazyFinalize ({lockToken} = {}) { async pAddBrewsLazyFinalize () {
let brewDocs = []; let unavailableSources = [];
try { try {
lockToken = await this._LOCK.pLock({token: lockToken}); const lockToken = await this._LOCK.pLock();
return (await this._pAddBrewsLazyFinalize_({lockToken})); ({brewDocs, unavailableSources} = await this._pAddBrewsLazyFinalize_({lockToken}));
} catch (e) {
JqueryUtil.doToast({type: "danger", content: `Failed to finalize ${this.DISPLAY_NAME_PLURAL}! ${VeCt.STR_SEE_CONSOLE}`});
setTimeout(() => { throw e; });
return [];
} finally { } finally {
this._LOCK.unlock(); this._LOCK.unlock();
} }
await this._pLoadParentDependencies({unavailableSources});
return brewDocs;
} }
async _pAddBrewsLazyFinalize_ ({lockToken}) { async _pAddBrewsLazyFinalize_ ({lockToken}) {
const brewsRaw = await this._pGetBrewRaw({lockToken}); const brewsRaw = await this._pGetBrewRaw({lockToken});
const brewDeps = await this._pGetBrewDependencies({brewDocs: this._addLazy_brewsTemp, brewsRaw, lockToken}); const {brewDocsDependencies, unavailableSources} = await this._pGetBrewDependencies({brewDocs: this._addLazy_brewsTemp, brewsRaw, lockToken});
const out = MiscUtil.copyFast(brewDeps); const brewDocs = MiscUtil.copyFast(brewDocsDependencies);
const brewsNxt = this._getNextBrews(MiscUtil.copyFast(brewsRaw), [...this._addLazy_brewsTemp, ...brewDeps]); const brewsNxt = this._getNextBrews(MiscUtil.copyFast(brewsRaw), [...this._addLazy_brewsTemp, ...brewDocsDependencies]);
await this.pSetBrew(brewsNxt, {lockToken}); await this.pSetBrew(brewsNxt, {lockToken});
this._addLazy_brewsTemp = []; this._addLazy_brewsTemp = [];
return out; return {brewDocs, unavailableSources};
} }
async pPullAllBrews ({brews} = {}) { async pPullAllBrews ({brews} = {}) {
@@ -1268,6 +1334,66 @@ class _BrewUtil2Base {
// endregion // endregion
} }
class _PrereleaseUtil extends _BrewUtil2Base {
_STORAGE_KEY_LEGACY = null;
_STORAGE_KEY_LEGACY_META = null;
_STORAGE_KEY = "PRERELEASE_STORAGE";
_STORAGE_KEY_META = "PRERELEASE_META_STORAGE";
_STORAGE_KEY_CUSTOM_URL = "PRERELEASE_CUSTOM_REPO_URL";
_STORAGE_KEY_MIGRATION_VERSION = "PRERELEASE_STORAGE_MIGRATION";
_PATH_LOCAL_DIR = "prerelease";
_PATH_LOCAL_INDEX = VeCt.JSON_PRERELEASE_INDEX;
_VERSION = 1;
IS_EDITABLE = false;
PAGE_MANAGE = UrlUtil.PG_MANAGE_PRERELEASE;
URL_REPO_DEFAULT = VeCt.URL_PRERELEASE;
DISPLAY_NAME = "prerelease content";
DISPLAY_NAME_PLURAL = "prereleases";
DEFAULT_AUTHOR = "Wizards of the Coast";
STYLE_BTN = "btn-primary";
IS_PREFER_DATE_ADDED = false;
/* -------------------------------------------- */
_pInit_doBindDragDrop () { /* No-op */ }
/* -------------------------------------------- */
async pGetSourceIndex (urlRoot) { return DataUtil.prerelease.pLoadSourceIndex(urlRoot); }
getFileUrl (path, urlRoot) { return DataUtil.prerelease.getFileUrl(path, urlRoot); }
pLoadTimestamps (brewIndex, src, urlRoot) { return DataUtil.prerelease.pLoadTimestamps(urlRoot); }
pLoadPropIndex (brewIndex, src, urlRoot) { return DataUtil.prerelease.pLoadPropIndex(urlRoot); }
pLoadMetaIndex (brewIndex, src, urlRoot) { return DataUtil.prerelease.pLoadMetaIndex(urlRoot); }
/* -------------------------------------------- */
// region Editable
pGetEditableBrewDoc (brew) { return super.pGetEditableBrewDoc(brew); }
pGetOrCreateEditableBrewDoc () { return super.pGetOrCreateEditableBrewDoc(); }
pSetEditableBrewDoc () { return super.pSetEditableBrewDoc(); }
pGetEditableBrewEntity (prop, uniqueId, {isDuplicate = false} = {}) { return super.pGetEditableBrewEntity(prop, uniqueId, {isDuplicate}); }
pPersistEditableBrewEntity (prop, ent) { return super.pPersistEditableBrewEntity(prop, ent); }
pRemoveEditableBrewEntity (prop, uniqueId) { return super.pRemoveEditableBrewEntity(prop, uniqueId); }
pAddSource (sourceObj) { return super.pAddSource(sourceObj); }
pEditSource (sourceObj) { return super.pEditSource(sourceObj); }
pIsEditableSourceJson (sourceJson) { return super.pIsEditableSourceJson(sourceJson); }
pMoveOrCopyToEditableBySourceJson (sourceJson) { return super.pMoveOrCopyToEditableBySourceJson(sourceJson); }
pMoveToEditable ({brews}) { return super.pMoveToEditable({brews}); }
pCopyToEditable ({brews}) { return super.pCopyToEditable({brews}); }
// endregion
}
class _BrewUtil2 extends _BrewUtil2Base { class _BrewUtil2 extends _BrewUtil2Base {
_STORAGE_KEY_LEGACY = "HOMEBREW_STORAGE"; _STORAGE_KEY_LEGACY = "HOMEBREW_STORAGE";
_STORAGE_KEY_LEGACY_META = "HOMEBREW_META_STORAGE"; _STORAGE_KEY_LEGACY_META = "HOMEBREW_META_STORAGE";
@@ -1291,6 +1417,7 @@ class _BrewUtil2 extends _BrewUtil2Base {
DISPLAY_NAME_PLURAL = "homebrews"; DISPLAY_NAME_PLURAL = "homebrews";
DEFAULT_AUTHOR = ""; DEFAULT_AUTHOR = "";
STYLE_BTN = "btn-info"; STYLE_BTN = "btn-info";
IS_PREFER_DATE_ADDED = true;
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -1344,7 +1471,7 @@ class _BrewUtil2 extends _BrewUtil2Base {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _pGetSourceIndex (urlRoot) { return DataUtil.brew.pLoadSourceIndex(urlRoot); } async pGetSourceIndex (urlRoot) { return DataUtil.brew.pLoadSourceIndex(urlRoot); }
getFileUrl (path, urlRoot) { return DataUtil.brew.getFileUrl(path, urlRoot); } getFileUrl (path, urlRoot) { return DataUtil.brew.getFileUrl(path, urlRoot); }
@@ -1518,67 +1645,8 @@ class _BrewUtil2 extends _BrewUtil2Base {
// endregion // endregion
} }
class _PrereleaseUtil extends _BrewUtil2Base {
_STORAGE_KEY_LEGACY = null;
_STORAGE_KEY_LEGACY_META = null;
_STORAGE_KEY = "PRERELEASE_STORAGE";
_STORAGE_KEY_META = "PRERELEASE_META_STORAGE";
_STORAGE_KEY_CUSTOM_URL = "PRERELEASE_CUSTOM_REPO_URL";
_STORAGE_KEY_MIGRATION_VERSION = "PRERELEASE_STORAGE_MIGRATION";
_PATH_LOCAL_DIR = "prerelease";
_PATH_LOCAL_INDEX = VeCt.JSON_PRERELEASE_INDEX;
_VERSION = 1;
IS_EDITABLE = false;
PAGE_MANAGE = UrlUtil.PG_MANAGE_PRERELEASE;
URL_REPO_DEFAULT = VeCt.URL_PRERELEASE;
DISPLAY_NAME = "prerelease content";
DISPLAY_NAME_PLURAL = "prereleases";
DEFAULT_AUTHOR = "Wizards of the Coast";
STYLE_BTN = "btn-primary";
/* -------------------------------------------- */
_pInit_doBindDragDrop () { /* No-op */ }
/* -------------------------------------------- */
async _pGetSourceIndex (urlRoot) { return DataUtil.prerelease.pLoadSourceIndex(urlRoot); }
getFileUrl (path, urlRoot) { return DataUtil.prerelease.getFileUrl(path, urlRoot); }
pLoadTimestamps (brewIndex, src, urlRoot) { return DataUtil.prerelease.pLoadTimestamps(urlRoot); }
pLoadPropIndex (brewIndex, src, urlRoot) { return DataUtil.prerelease.pLoadPropIndex(urlRoot); }
pLoadMetaIndex (brewIndex, src, urlRoot) { return DataUtil.prerelease.pLoadMetaIndex(urlRoot); }
/* -------------------------------------------- */
// region Editable
pGetEditableBrewDoc (brew) { return super.pGetEditableBrewDoc(brew); }
pGetOrCreateEditableBrewDoc () { return super.pGetOrCreateEditableBrewDoc(); }
pSetEditableBrewDoc () { return super.pSetEditableBrewDoc(); }
pGetEditableBrewEntity (prop, uniqueId, {isDuplicate = false} = {}) { return super.pGetEditableBrewEntity(prop, uniqueId, {isDuplicate}); }
pPersistEditableBrewEntity (prop, ent) { return super.pPersistEditableBrewEntity(prop, ent); }
pRemoveEditableBrewEntity (prop, uniqueId) { return super.pRemoveEditableBrewEntity(prop, uniqueId); }
pAddSource (sourceObj) { return super.pAddSource(sourceObj); }
pEditSource (sourceObj) { return super.pEditSource(sourceObj); }
pIsEditableSourceJson (sourceJson) { return super.pIsEditableSourceJson(sourceJson); }
pMoveOrCopyToEditableBySourceJson (sourceJson) { return super.pMoveOrCopyToEditableBySourceJson(sourceJson); }
pMoveToEditable ({brews}) { return super.pMoveToEditable({brews}); }
pCopyToEditable ({brews}) { return super.pCopyToEditable({brews}); }
// endregion
}
globalThis.BrewUtil2 = new _BrewUtil2();
globalThis.PrereleaseUtil = new _PrereleaseUtil(); globalThis.PrereleaseUtil = new _PrereleaseUtil();
globalThis.BrewUtil2 = new _BrewUtil2({parent: globalThis.PrereleaseUtil}); // Homebrew can depend on prerelease, but not the other way around
class ManageBrewUi { class ManageBrewUi {
static _RenderState = class { static _RenderState = class {
@@ -2496,13 +2564,16 @@ class GetBrewUi {
} }
static mutateForFilters (brewInfo) { static mutateForFilters (brewInfo) {
if (brewInfo._brewAuthor && brewInfo._brewAuthor.toLowerCase().startsWith("sample -")) brewInfo._fMisc = ["Sample"]; brewInfo._fMisc = [];
if (brewInfo._brewAuthor && brewInfo._brewAuthor.toLowerCase().startsWith("sample -")) brewInfo._fMisc.push("Sample");
if (brewInfo.sources?.some(ab => ab.startsWith(Parser.SRC_UA_ONE_PREFIX))) brewInfo._fMisc.push("One D&D");
} }
addToFilters (it, isExcluded) { addToFilters (it, isExcluded) {
if (isExcluded) return; if (isExcluded) return;
this._typeFilter.addItem(it.props); this._typeFilter.addItem(it.props);
this._miscFilter.addItem(it._fMisc);
} }
async _pPopulateBoxOptions (opts) { async _pPopulateBoxOptions (opts) {
@@ -2555,6 +2626,7 @@ class GetBrewUi {
case "category": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSortLower, "_brewPropDisplayName"); case "category": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSortLower, "_brewPropDisplayName");
case "added": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSort, "_brewAdded"); case "added": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSort, "_brewAdded");
case "modified": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSort, "_brewModified"); case "modified": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSort, "_brewModified");
case "published": return this.constructor._sortUrlList_orFallback(a, b, SortUtil.ascSort, "_brewPublished");
default: throw new Error(`No sort order defined for property "${o.sortBy}"`); default: throw new Error(`No sort order defined for property "${o.sortBy}"`);
} }
} }
@@ -2573,10 +2645,11 @@ class GetBrewUi {
async pInit () { async pInit () {
const urlRoot = await this._brewUtil.pGetCustomUrl(); const urlRoot = await this._brewUtil.pGetCustomUrl();
const [timestamps, propIndex, metaIndex] = await Promise.all([ const [timestamps, propIndex, metaIndex, sourceIndex] = await Promise.all([
this._brewUtil.pLoadTimestamps(urlRoot), this._brewUtil.pLoadTimestamps(urlRoot),
this._brewUtil.pLoadPropIndex(urlRoot), this._brewUtil.pLoadPropIndex(urlRoot),
this._brewUtil.pLoadMetaIndex(urlRoot), this._brewUtil.pLoadMetaIndex(urlRoot),
this._brewUtil.pGetSourceIndex(urlRoot),
]); ]);
const pathToMeta = {}; const pathToMeta = {};
@@ -2589,6 +2662,12 @@ class GetBrewUi {
}); });
}); });
Object.entries(sourceIndex)
.forEach(([src, path]) => {
if (!pathToMeta[path]) return;
(pathToMeta[path].sources ||= []).push(src);
});
this._dataList = Object.entries(pathToMeta) this._dataList = Object.entries(pathToMeta)
.map(([path, meta]) => { .map(([path, meta]) => {
const out = { const out = {
@@ -2597,6 +2676,7 @@ class GetBrewUi {
name: UrlUtil.getFilename(path), name: UrlUtil.getFilename(path),
dirProp: this._brewUtil.getDirProp(meta.dir), dirProp: this._brewUtil.getDirProp(meta.dir),
props: meta.props, props: meta.props,
sources: meta.sources,
}; };
const spl = out.name.trim().replace(/\.json$/, "").split(";").map(it => it.trim()); const spl = out.name.trim().replace(/\.json$/, "").split(";").map(it => it.trim());
@@ -2610,6 +2690,7 @@ class GetBrewUi {
out._brewAdded = timestamps[out.path]?.a ?? 0; out._brewAdded = timestamps[out.path]?.a ?? 0;
out._brewModified = timestamps[out.path]?.m ?? 0; out._brewModified = timestamps[out.path]?.m ?? 0;
out._brewPublished = timestamps[out.path]?.p ?? 0;
out._brewInternalSources = metaIndex[out.name]?.n || []; out._brewInternalSources = metaIndex[out.name]?.n || [];
out._brewStatus = metaIndex[out.name]?.s || "ready"; out._brewStatus = metaIndex[out.name]?.s || "ready";
out._brewPropDisplayName = this._brewUtil.getPropDisplayName(out.dirProp); out._brewPropDisplayName = this._brewUtil.getPropDisplayName(out.dirProp);
@@ -2662,13 +2743,17 @@ class GetBrewUi {
const $wrpMiniPills = $(`<div class="fltr__mini-view btn-group"></div>`); 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>`;
const $wrpSort = $$`<div class="filtertools manbrew__filtertools btn-group input-group input-group--bottom ve-flex no-shrink"> 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> <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-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-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-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> <button class="col-1-4 sort btn btn-default btn-xs" data-sort="modified">Modified</button>
<button class="col-1-4 sort btn btn-default btn-xs" data-sort="added">Added</button> ${btnSortAddedPublished}
<button class="sort btn btn-default btn-xs ve-grow" disabled>Source</button> <button class="sort btn btn-default btn-xs ve-grow" disabled>Source</button>
</div>`; </div>`;
@@ -2747,8 +2832,9 @@ class GetBrewUi {
} }
_pRender_getUrlRowMeta (rdState, brewInfo, ix) { _pRender_getUrlRowMeta (rdState, brewInfo, ix) {
const timestampAdded = brewInfo._brewAdded const epochAddedPublished = this._brewUtil.IS_PREFER_DATE_ADDED ? brewInfo._brewAdded : brewInfo._brewPublished;
? DatetimeUtil.getDateStr({date: new Date(brewInfo._brewAdded * 1000), isShort: true, isPad: true}) const timestampAddedPublished = epochAddedPublished
? DatetimeUtil.getDateStr({date: new Date(epochAddedPublished * 1000), isShort: true, isPad: true})
: ""; : "";
const timestampModified = brewInfo._brewModified const timestampModified = brewInfo._brewModified
? DatetimeUtil.getDateStr({date: new Date(brewInfo._brewModified * 1000), isShort: true, isPad: true}) ? DatetimeUtil.getDateStr({date: new Date(brewInfo._brewModified * 1000), isShort: true, isPad: true})
@@ -2784,7 +2870,7 @@ class GetBrewUi {
e_({tag: "span", clazz: "col-3", text: brewInfo._brewAuthor}), 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-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: timestampModified}),
e_({tag: "span", clazz: "col-1-4 ve-text-center code", text: timestampAdded}), e_({tag: "span", clazz: "col-1-4 ve-text-center code", text: timestampAddedPublished}),
e_({ e_({
tag: "span", tag: "span",
clazz: "col-1 manbrew__source ve-text-center pr-0", clazz: "col-1 manbrew__source ve-text-center pr-0",

View File

@@ -177,6 +177,7 @@ PropOrder._MONSTER = [
"rest", "rest",
"daily", "daily",
"weekly", "weekly",
"monthly",
"yearly", "yearly",
"recharge", "recharge",
"charges", "charges",

View File

@@ -2,7 +2,7 @@
// in deployment, `IS_DEPLOYED = "<version number>";` should be set below. // in deployment, `IS_DEPLOYED = "<version number>";` should be set below.
globalThis.IS_DEPLOYED = undefined; globalThis.IS_DEPLOYED = undefined;
globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.197.3"/* 5ETOOLS_VERSION__CLOSE */; globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.197.4"/* 5ETOOLS_VERSION__CLOSE */;
globalThis.DEPLOYED_IMG_ROOT = undefined; globalThis.DEPLOYED_IMG_ROOT = undefined;
// for the roll20 script to set // for the roll20 script to set
globalThis.IS_VTT = false; globalThis.IS_VTT = false;
@@ -3108,17 +3108,6 @@ if (!IS_DEPLOYED && !IS_VTT && typeof window !== "undefined") {
} }
} }
}); });
// TODO(img) remove this in future
window.addEventListener("load", () => {
if (window.location?.host === "5etools-mirror-1.github.io") {
JqueryUtil.doToast({
type: "warning",
isAutoHide: false,
content: $(`<div>This mirror is no longer being updated/maintained, and will be shut down on March 1st 2024.<br>Please use <a href="https://5etools-mirror-2.github.io/" rel="noopener noreferrer">5etools-mirror-2.github.io</a> instead, and <a href="https://gist.github.com/5etools-mirror-2/40d6d80f40205882d3fa5006fae963a4" rel="noopener noreferrer">migrate your data</a>.</div>`),
});
}
});
} }
// SORTING ============================================================================================================= // SORTING =============================================================================================================
@@ -4359,7 +4348,7 @@ globalThis.DataUtil = {
modInfo[prop].forEach(sp => (spellcasting[prop] = spellcasting[prop] || []).push(sp)); modInfo[prop].forEach(sp => (spellcasting[prop] = spellcasting[prop] || []).push(sp));
}); });
["recharge", "charges", "rest", "daily", "weekly", "yearly"].forEach(prop => { ["recharge", "charges", "rest", "daily", "weekly", "monthly", "yearly"].forEach(prop => {
if (!modInfo[prop]) return; if (!modInfo[prop]) return;
for (let i = 1; i <= 9; ++i) { for (let i = 1; i <= 9; ++i) {
@@ -4442,7 +4431,7 @@ globalThis.DataUtil = {
spellcasting[prop].filter(it => !modInfo[prop].includes(it)); spellcasting[prop].filter(it => !modInfo[prop].includes(it));
}); });
["recharge", "charges", "rest", "daily", "weekly", "yearly"].forEach(prop => { ["recharge", "charges", "rest", "daily", "weekly", "monthly", "yearly"].forEach(prop => {
if (!modInfo[prop]) return; if (!modInfo[prop]) return;
for (let i = 1; i <= 9; ++i) { for (let i = 1; i <= 9; ++i) {
@@ -7567,4 +7556,15 @@ if (!IS_VTT && typeof window !== "undefined") {
// $(`.cancer__sidebar-rhs-inner--top`).append(`<div class="TEST_RHS_TOP"></div>`) // $(`.cancer__sidebar-rhs-inner--top`).append(`<div class="TEST_RHS_TOP"></div>`)
// $(`.cancer__sidebar-rhs-inner--bottom`).append(`<div class="TEST_RHS_BOTTOM"></div>`) // $(`.cancer__sidebar-rhs-inner--bottom`).append(`<div class="TEST_RHS_BOTTOM"></div>`)
// }); // });
// TODO(img) remove this in future
window.addEventListener("load", () => {
if (window.location?.host !== "5etools-mirror-1.github.io") return;
JqueryUtil.doToast({
type: "warning",
isAutoHide: false,
content: $(`<div>This mirror is no longer being updated/maintained, and will be shut down on March 1st 2024.<br>Please use <a href="https://5etools-mirror-2.github.io/" rel="noopener noreferrer">5etools-mirror-2.github.io</a> instead, and <a href="https://gist.github.com/5etools-mirror-2/40d6d80f40205882d3fa5006fae963a4" rel="noopener noreferrer">migrate your data</a>.</div>`),
});
});
} }

18
package-lock.json generated
View File

@@ -1,15 +1,15 @@
{ {
"name": "5etools", "name": "5etools",
"version": "1.197.3", "version": "1.197.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "5etools", "name": "5etools",
"version": "1.197.3", "version": "1.197.4",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"5etools-utils": "^0.9.56", "5etools-utils": "^0.9.57",
"ajv": "^8.11.2", "ajv": "^8.11.2",
"ajv-formats": "^2.1.1", "ajv-formats": "^2.1.1",
"commander": "^11.0.0", "commander": "^11.0.0",
@@ -2692,9 +2692,9 @@
"dev": true "dev": true
}, },
"node_modules/5etools-utils": { "node_modules/5etools-utils": {
"version": "0.9.56", "version": "0.9.57",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.9.56.tgz", "resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.9.57.tgz",
"integrity": "sha512-944ivhqzlirhGd9FLXJOxpflo8mSSOBN7tJnyniWWrgy+WUd1FpDs4n6JvYbOAc8HOhx+ryGMZJY8E7Zc91ZJw==", "integrity": "sha512-PuHzfVhKzo3nIBncoadMysIvUBOLhW+L4SR1qpYzxsYEdqsv5BKdptbTPhe+5jaVqYNQKxQhqcJemkpqEB1mTw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ajv": "^8.12.0", "ajv": "^8.12.0",
@@ -11133,9 +11133,9 @@
"dev": true "dev": true
}, },
"5etools-utils": { "5etools-utils": {
"version": "0.9.56", "version": "0.9.57",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.9.56.tgz", "resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.9.57.tgz",
"integrity": "sha512-944ivhqzlirhGd9FLXJOxpflo8mSSOBN7tJnyniWWrgy+WUd1FpDs4n6JvYbOAc8HOhx+ryGMZJY8E7Zc91ZJw==", "integrity": "sha512-PuHzfVhKzo3nIBncoadMysIvUBOLhW+L4SR1qpYzxsYEdqsv5BKdptbTPhe+5jaVqYNQKxQhqcJemkpqEB1mTw==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^8.12.0", "ajv": "^8.12.0",

View File

@@ -1,7 +1,7 @@
{ {
"name": "5etools", "name": "5etools",
"author": "TheGiddyLimit", "author": "TheGiddyLimit",
"version": "1.197.3", "version": "1.197.4",
"license": "MIT", "license": "MIT",
"description": "A site dedicated to making playing games with your friends as easy as possible.", "description": "A site dedicated to making playing games with your friends as easy as possible.",
"type": "module", "type": "module",
@@ -42,7 +42,7 @@
"url": "git+https://github.com/5etools-mirror-2/5etools-mirror-2.github.io.git" "url": "git+https://github.com/5etools-mirror-2/5etools-mirror-2.github.io.git"
}, },
"devDependencies": { "devDependencies": {
"5etools-utils": "^0.9.56", "5etools-utils": "^0.9.57",
"ajv": "^8.11.2", "ajv": "^8.11.2",
"ajv-formats": "^2.1.1", "ajv-formats": "^2.1.1",
"commander": "^11.0.0", "commander": "^11.0.0",

View File

@@ -81,4 +81,18 @@
} }
} }
} }
&-homebrew__ {
&b {
background-color: $rgb-bg-homebrew--night;
}
&disp-old-content {
color: #f99;
}
&disp-inline {
background-color: $rgb-bg-homebrew--night;
}
}
} }

View File

@@ -630,6 +630,39 @@ $rgb-inset-border: #656565;
} }
} }
} }
&-homebrew__ {
&b {
background-color: $rgb-bg-homebrew;
clear: both;
}
&wrp-notice {
float: right;
border: 1px dotted;
margin-bottom: 5px;
margin-left: 5px;
padding-right: 2px;
padding-left: 2px;
text-indent: 0;
}
&disp-notice {
&::before {
content: "Homebrew";
}
}
&disp-old-content {
color: #a00;
margin-left: 5px;
}
&disp-inline {
background-color: $rgb-bg-homebrew;
text-decoration: underline dotted;
}
}
} }
// Entries embedded in tables // Entries embedded in tables

View File

@@ -2197,46 +2197,6 @@ th.border {
border: 1px solid rgba(0, 0, 0, 0.15); border: 1px solid rgba(0, 0, 0, 0.15);
} }
.homebrew-section {
background-color: $rgb-bg-homebrew;
.homebrew-float {
float: right;
border: 1px dotted;
margin-bottom: 5px;
margin-left: 5px;
padding-right: 2px;
padding-left: 2px;
text-indent: 0;
}
.homebrew-old-content {
color: #a00;
margin-left: 5px;
}
.homebrew-notice {
&::before {
content: "Homebrew";
}
}
}
.homebrew-inline {
background-color: $rgb-bg-homebrew;
text-decoration: underline dotted;
}
.stats tr.homebrew-hover {
> td {
padding: 5px 0.3em;
> div > *:last-child {
margin-bottom: 0;
}
}
}
.toast { .toast {
@include shadow-1(); @include shadow-1();
@@ -3138,18 +3098,6 @@ th.border {
background: $rgb-bg--night; background: $rgb-bg--night;
} }
.homebrew-section {
background-color: $rgb-bg-homebrew--night;
.homebrew-old-content {
color: #f99;
}
}
.homebrew-inline {
background-color: $rgb-bg-homebrew--night;
}
.highlight { .highlight {
color: $rgb-bg--night; color: $rgb-bg--night;
background-color: $rgb-bg-highlight--night; background-color: $rgb-bg-highlight--night;

View File

@@ -87,7 +87,7 @@ $rgb-cb-grey: #666;
$rgb-bg: white; $rgb-bg: white;
$rgb-bg--alt: whitesmoke; $rgb-bg--alt: whitesmoke;
$rgb-bg-homebrew: rgba(255, 0, 0, 0.1); $rgb-bg-homebrew: #ff00001a;
$rgb-bg--night: #222; $rgb-bg--night: #222;
$rgb-bg--alt-night: #383838; $rgb-bg--alt-night: #383838;

File diff suppressed because one or more lines are too long