This commit is contained in:
TheGiddyLimit
2024-05-06 22:24:37 +01:00
parent 99cb60d804
commit 9c8ae15ff7
113 changed files with 1022 additions and 852 deletions

View File

@@ -641,7 +641,7 @@ class BlocklistUi {
}
async _pImport_getUserUpload () {
return DataUtil.pUserUpload({expectedFileTypes: ["content-blocklist", "content-blacklist"]}); // Supports old fileType "content-blacklist"
return InputUiUtil.pGetUserUploadJson({expectedFileTypes: ["content-blocklist", "content-blacklist"]}); // Supports old fileType "content-blacklist"
}
async _pImport (evt) {

View File

@@ -831,7 +831,7 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
$(`.cls-main__sc-fluff`)
.each((i, e) => {
const $e = $(e);
$e.toggleVe(!!this._state[$e.attr("data-subclass-id")]);
$e.toggleVe(!!this._state[$e.attr("data-subclass-id-fluff")]);
});
}
};
@@ -2183,7 +2183,7 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
if (!rdScFluff?.length) return;
$(`<tr class="cls-main__sc-fluff" data-subclass-id="${UrlUtil.getStateKeySubclass(sc)}"><td colspan="6"></td></tr>`)
$(`<tr class="cls-main__sc-fluff" data-subclass-id-fluff="${UrlUtil.getStateKeySubclass(sc)}"><td colspan="6"></td></tr>`)
.fastSetHtml(rdScFluff)
.appendTo($content);
});

View File

@@ -720,7 +720,7 @@ class CreatureParser extends BaseParser {
.map(it => {
if (!it.name.trim() && !it.entries.length) return null;
const m = /can take (\d) legendary actions/gi.exec(it.entries[0]);
const m = /can take (\d) legendary actions?/gi.exec(it.entries[0]);
if (!it.name.trim() && m) {
if (m[1] !== "3") stats.legendaryActions = Number(m[1]);
return null;
@@ -1503,6 +1503,13 @@ class CreatureParser extends BaseParser {
};
}
if (/ (&|and) /.test(strType)) {
const pts = strType.split(/(?:, |,? (?:&|and) )/g);
type = {
choose: pts.map(it => it.trim()).filter(Boolean),
};
}
if (!type) type = strType;
if (typeof type === "string" && !tags && !note) return type;

View File

@@ -374,6 +374,7 @@ class _CreatureImmunityResistanceVulnerabilityConverterBase {
static _getCleanIpt ({ipt}) {
return ipt
.replace(/^none\b/i, "") // Thanks.
.replace(/\.+\s*$/, "")
.trim()
;
}
@@ -463,7 +464,12 @@ class _CreatureImmunityResistanceVulnerabilityConverterBase {
pt = pt.slice(ixPreNote).trim();
}
if (pt) newGroup.push(pt);
pt = pt.trim();
if (!pt) return;
pt
.split(/ and /g)
.forEach(val => newGroup.push(val));
});
const newGroupOut = newGroup

View File

@@ -806,7 +806,7 @@ class SideMenu {
});
const $btnLoadFile = $(`<button class="btn btn-primary">Load from File</button>`).appendTo($wrpSaveLoadFile);
$btnLoadFile.on("click", async () => {
const {jsons, errors} = await DataUtil.pUserUpload({expectedFileTypes: ["dm-screen"]});
const {jsons, errors} = await InputUiUtil.pGetUserUploadJson({expectedFileTypes: ["dm-screen"]});
DataUtil.doHandleFileLoadErrorsGeneric(errors);

View File

@@ -534,7 +534,7 @@ class SublistManager {
}
async pHandleClick_upload ({isAdditive = false} = {}) {
const {jsons, errors} = await DataUtil.pUserUpload({expectedFileTypes: this._getUploadFileTypes()});
const {jsons, errors} = await InputUiUtil.pGetUserUploadJson({expectedFileTypes: this._getUploadFileTypes()});
DataUtil.doHandleFileLoadErrorsGeneric(errors);
@@ -1691,7 +1691,7 @@ class ListPage {
this._btnsTabs[ident] = e_({
tag: "button",
clazz: "ui-tab__btn-tab-head btn btn-default",
clazz: "ui-tab__btn-tab-head btn btn-default pt-2p px-4p pb-0",
children: [
e_({
tag: "span",
@@ -2034,6 +2034,13 @@ class ListPage {
},
};
// See: https://github.com/1904labs/dom-to-image-more/issues/146
if (BrowserUtil.isFirefox()) {
const bcr = this._$pgContent[0].getBoundingClientRect();
optsDomToImage.width = bcr.width;
optsDomToImage.height = bcr.height;
}
if (isFast) {
let blob;
try {

View File

@@ -1993,7 +1993,7 @@ class LootGenMagicItem extends BaseComponent {
const $btnReroll = this._$getBtnReroll();
return $$`<li class="split-v-center">
<div class="ve-flex-v-center ve-flex-wrap pr-3">
<div class="ve-flex-v-center ve-flex-wrap pr-3 min-w-0">
${$dispBaseEntry}
${$dispRoll}
</div>
@@ -2087,7 +2087,7 @@ class LootGenMagicItemSpellScroll extends LootGenMagicItem {
const $btnReroll = this._$getBtnReroll();
return $$`<li class="split-v-center">
<div class="ve-flex-v-center ve-flex-wrap pr-3">
<div class="ve-flex-v-center ve-flex-wrap pr-3 min-w-0">
${$dispBaseEntry}
<div class="ve-flex-v-center italic mr-2">
<span>(</span>
@@ -2141,7 +2141,7 @@ class LootGenMagicItemSubItems extends LootGenMagicItem {
const $btnReroll = this._$getBtnReroll();
return $$`<li class="split-v-center">
<div class="ve-flex-v-center ve-flex-wrap pr-3">
<div class="ve-flex-v-center ve-flex-wrap pr-3 min-w-0">
${$dispBaseEntry}
<div class="ve-flex-v-center italic mr-2">
<span>(</span>
@@ -2211,14 +2211,14 @@ class LootGenMagicItemTable extends LootGenMagicItem {
return $$`<li class="ve-flex-col">
<div class="split-v-center">
<div class="ve-flex-v-center ve-flex-wrap pr-3">
<div class="ve-flex-v-center ve-flex-wrap pr-3 min-w-0">
${$dispBaseEntry}
${$dispRoll}
</div>
${$btnReroll}
</div>
<div class="split-v-center pl-2">
<div class="ve-flex-v-center ve-flex-wrap pr-3">
<div class="ve-flex-v-center ve-flex-wrap pr-3 min-w-0">
<span class="ml-1 mr-2">&rarr;</span>
${$dispTableEntry}
${$dispTableRoll}

View File

@@ -50,6 +50,7 @@ class MapsUtil {
? {
[head.id]: {
id: head.id,
name: head.name,
source: head.source,
prop,
parentSource: head.parentSource,

View File

@@ -85,9 +85,9 @@ class MapsPage extends BaseComponent {
const mapData = {};
// Apply the prerelease/brew data first, so the "official" data takes precedence, where required
Object.assign(mapData, MiscUtil.copy(await this._pGetPrereleaseBrewMaps({brewUtil: BrewUtil2})));
Object.assign(mapData, MiscUtil.copy(await this._pGetPrereleaseBrewMaps({brewUtil: PrereleaseUtil})));
Object.assign(mapData, MiscUtil.copy(mapDataBase));
Object.assign(mapData, MiscUtil.copyFast(await this._pGetPrereleaseBrewMaps({brewUtil: BrewUtil2})));
Object.assign(mapData, MiscUtil.copyFast(await this._pGetPrereleaseBrewMaps({brewUtil: PrereleaseUtil})));
Object.assign(mapData, MiscUtil.copyFast(mapDataBase));
return mapData;
}
@@ -134,8 +134,8 @@ class MapsPage extends BaseComponent {
propsDisplaySource.push(propDisplaySource);
const shortNameHtml = this._getShortNameHtml({source, sourceMeta});
const titleName = this._getTitleName({source, sourceMeta});
const searchName = this._getSearchName({source, sourceMeta});
const titleName = this._getTitleName({sourceMeta});
const searchName = this._getSearchName({sourceMeta});
const propsDisplayChapter = [];
const rendersChapter = sourceMeta.chapters
@@ -248,19 +248,31 @@ class MapsPage extends BaseComponent {
}
_getShortNameHtml ({source, sourceMeta}) {
if (!sourceMeta.parentSource) return Parser.sourceJsonToFull(source).qq();
const fullSource = Parser.sourceJsonToFull(source);
const titleName = this._getTitleName({sourceMeta});
if (!sourceMeta.parentSource) return titleName.qq();
const fullParentSource = Parser.sourceJsonToFull(sourceMeta.parentSource);
return fullSource.replace(new RegExp(`^${fullParentSource.escapeRegexp()}: `, "i"), `<span title="${Parser.sourceJsonToFull(sourceMeta.parentSource).qq()}">${Parser.sourceJsonToAbv(sourceMeta.parentSource).qq()}</span>: `);
const ptPrefixParent = `<span title="${Parser.sourceJsonToFull(sourceMeta.parentSource).qq()}">${Parser.sourceJsonToAbv(sourceMeta.parentSource).qq()}</span>: `;
let isIncludesParent = false;
let out = titleName
.replace(new RegExp(`^${fullParentSource.escapeRegexp()}: `, "i"), () => {
isIncludesParent = true;
return ptPrefixParent;
});
if (isIncludesParent) return out;
return `${ptPrefixParent}${out}`;
}
_getTitleName ({source, sourceMeta}) {
if (!sourceMeta.parentSource) return Parser.sourceJsonToFull(source).toLowerCase().trim();
return `${Parser.sourceJsonToFull(sourceMeta.parentSource)}: ${Parser.sourceJsonToFull(source)}`.toLowerCase().trim();
_getTitleName ({sourceMeta}) {
if (sourceMeta.name) return sourceMeta.name;
return Parser.sourceJsonToFull(sourceMeta.source).trim();
}
_getSearchName ({source, sourceMeta}) {
return this._getTitleName({source, sourceMeta}).toLowerCase().trim();
_getSearchName ({sourceMeta}) {
return this._getTitleName({sourceMeta}).toLowerCase().trim();
}
_isVisibleSourceSearch ({searchName}) { return searchName.includes(this._state.search.trim().toLowerCase()); }

View File

@@ -785,7 +785,7 @@ NavBar.InteractionManager = class {
static async _pOnClick_button_loadStateFile (evt) {
evt.preventDefault();
const {jsons, errors} = await DataUtil.pUserUpload({expectedFileTypes: ["5etools"]});
const {jsons, errors} = await InputUiUtil.pGetUserUploadJson({expectedFileTypes: ["5etools"]});
DataUtil.doHandleFileLoadErrorsGeneric(errors);

View File

@@ -645,7 +645,8 @@ Parser.sourceJsonToDate = function (source) {
};
Parser.sourceJsonToColor = function (source) {
return `source__${source}`;
const sourceCased = Parser.sourceJsonToJson(source);
return `source__${sourceCased}`;
};
Parser.sourceJsonToStyle = function (source) {

View File

@@ -1098,7 +1098,7 @@ Renderer.dice.lang = {
.replace(/\s*?\bdivided by\b\s*?/g, " / ")
// endregion
.replace(/\s+/g, "")
.replace(/[\u2012\u2013\u2014]/g, "-") // convert dashes
.replace(/[\u2012\u2013\u2014\u2212]/g, "-") // convert dashes
.replace(/[×]/g, "*") // convert mult signs
.replace(/\*\*/g, "^") // convert ** to ^
.replace(/÷/g, "/") // convert div signs

View File

@@ -3000,7 +3000,7 @@ Renderer.utils = class {
tabButtons.forEach((tb, i) => {
tb.ix = i;
tb.$t = $(`<button class="ui-tab__btn-tab-head btn btn-default stat-tab-gen">${tb.label}</button>`)
tb.$t = $(`<button class="ui-tab__btn-tab-head btn btn-default stat-tab-gen pt-2p px-4p pb-0">${tb.label}</button>`)
.click(() => tb.fnActivateTab({isUserInput: true}));
tb.fnActivateTab = ({isUserInput = false} = {}) => {

View File

@@ -63,7 +63,7 @@ class StatGenPage {
html: `<span class="glyphicon glyphicon-upload"></span>`,
title: "Load from File",
pFnClick: async () => {
const {jsons, errors} = await DataUtil.pUserUpload({expectedFileTypes: ["statgen"]});
const {jsons, errors} = await InputUiUtil.pGetUserUploadJson({expectedFileTypes: ["statgen"]});
DataUtil.doHandleFileLoadErrorsGeneric(errors);

View File

@@ -149,8 +149,8 @@ StyleSwitcher._STORAGE_WIDE = "StyleSwitcher_style-wide";
StyleSwitcher._STYLE_DAY = "day";
StyleSwitcher._STYLE_NIGHT = "night";
StyleSwitcher._STYLE_NIGHT_ALT = "nightAlt";
StyleSwitcher._NIGHT_CLASS = "night-mode";
StyleSwitcher._NIGHT_CLASS_ALT = "night-mode--alt";
StyleSwitcher._NIGHT_CLASS = "ve-night-mode";
StyleSwitcher._NIGHT_CLASS_ALT = "ve-night-mode--alt";
StyleSwitcher._WIDE_ID = "style-switch__wide";
try {

View File

@@ -1833,7 +1833,7 @@ class ManageBrewUi {
}
async _pHandleClick_btnLoadFromFile (rdState) {
const {files, errors} = await DataUtil.pUserUpload({isMultiple: true});
const {files, errors} = await InputUiUtil.pGetUserUploadJson({isMultiple: true});
DataUtil.doHandleFileLoadErrorsGeneric(errors);

View File

@@ -847,6 +847,14 @@ class _DataTypeLoaderOptionalfeatureFluff extends _DataTypeLoaderSingleSource {
_filename = "fluff-optionalfeatures.json";
}
class _DataTypeLoaderRewardFluff extends _DataTypeLoaderSingleSource {
static PROPS = ["rewardFluff"];
static PAGE = UrlUtil.PG_REWARDS;
static IS_FLUFF = true;
_filename = "fluff-rewards.json";
}
class _DataTypeLoaderItemFluff extends _DataTypeLoaderSingleSource {
static PROPS = ["itemFluff"];
static PAGE = UrlUtil.PG_ITEMS;
@@ -1743,6 +1751,7 @@ class DataLoader {
_DataTypeLoaderBackgroundFluff.register({fnRegister});
_DataTypeLoaderFeatFluff.register({fnRegister});
_DataTypeLoaderOptionalfeatureFluff.register({fnRegister});
_DataTypeLoaderRewardFluff.register({fnRegister});
_DataTypeLoaderItemFluff.register({fnRegister});
_DataTypeLoaderRaceFluff.register({fnRegister});
_DataTypeLoaderLanguageFluff.register({fnRegister});

View File

@@ -219,7 +219,7 @@ class ListUtilEntity {
...others
},
) {
const {jsons, errors} = await DataUtil.pUserUpload({
const {jsons, errors} = await InputUiUtil.pGetUserUploadJson({
expectedFileTypes: this._getFileTypes({page}),
});
@@ -516,7 +516,7 @@ class SaveManager extends BaseComponent {
? null
: $(`<button class="btn btn-default btn-xs" title="Load Lists from File">Import All</button>`)
.click(async () => {
const {jsons, errors} = await DataUtil.pUserUpload({
const {jsons, errors} = await InputUiUtil.pGetUserUploadJson({
expectedFileTypes: [ListUtil.getDownloadFiletypeSaves({page: this._page})],
});

View File

@@ -1353,7 +1353,7 @@ class TabUiUtil extends TabUiUtilBase {
super.decorate(obj, {isInitMeta});
obj.__$getBtnTab = function ({tabMeta, _propProxy, propActive, ixTab}) {
return $(`<button class="btn btn-default ui-tab__btn-tab-head ${tabMeta.isHeadHidden ? "ve-hidden" : ""}">${tabMeta.name.qq()}</button>`)
return $(`<button class="btn btn-default ui-tab__btn-tab-head pt-2p px-4p pb-0 ${tabMeta.isHeadHidden ? "ve-hidden" : ""}">${tabMeta.name.qq()}</button>`)
.click(() => obj[_propProxy][propActive] = ixTab);
};
@@ -1370,7 +1370,7 @@ class TabUiUtil extends TabUiUtilBase {
obj.__renderTypedTabMeta_buttons = function ({tabMeta, ixTab}) {
const $btns = tabMeta.buttons.map((meta, j) => {
const $btn = $(`<button class="btn ui-tab__btn-tab-head ${meta.type ? `btn-${meta.type}` : "btn-primary"}" ${meta.title ? `title="${meta.title.qq()}"` : ""}>${meta.html}</button>`)
const $btn = $(`<button class="btn ui-tab__btn-tab-head pt-2p px-4p pb-0 bbr-0 bbl-0 ${meta.type ? `btn-${meta.type}` : "btn-primary"}" ${meta.title ? `title="${meta.title.qq()}"` : ""}>${meta.html}</button>`)
.click(evt => meta.pFnClick(evt, $btn));
return $btn;
});
@@ -2633,7 +2633,7 @@ class InputUiUtil {
if (!isDataEntered) return null;
const outRaw = $iptNumber.val();
if (!outRaw.trim()) return null;
let out = UiUtil.strToInt(outRaw);
let out = UiUtil.strToNumber(outRaw);
if (opts.min) out = Math.max(opts.min, out);
if (opts.max) out = Math.min(opts.max, out);
if (opts.int) out = Math.round(out);
@@ -3307,6 +3307,76 @@ class InputUiUtil {
return Parser.CRS[comp._state.cur];
// endregion
}
/**
* Always returns an array of files, even in "single" mode.
* @param {?boolean} isMultiple
* @param {?Array<string>} expectedFileTypes
* @param {?string} propVersion
*/
static pGetUserUploadJson (
{
isMultiple = false,
expectedFileTypes = null,
propVersion = "siteVersion",
} = {},
) {
return new Promise(resolve => {
const $iptAdd = $(`<input type="file" ${isMultiple ? "multiple" : ""} class="ve-hidden" accept=".json">`)
.on("change", (evt) => {
const input = evt.target;
const reader = new FileReader();
let readIndex = 0;
const out = [];
const errs = [];
reader.onload = async () => {
const name = input.files[readIndex - 1].name;
const text = reader.result;
try {
const json = JSON.parse(text);
const isSkipFile = expectedFileTypes != null
&& json.fileType
&& !expectedFileTypes.includes(json.fileType)
&& !(await InputUiUtil.pGetUserBoolean({
textYes: "Yes",
textNo: "Cancel",
title: "File Type Mismatch",
htmlDescription: `The file "${name}" has the type "${json.fileType}" when the expected file type was "${expectedFileTypes.join("/")}".<br>Are you sure you want to upload this file?`,
}));
if (!isSkipFile) {
delete json.fileType;
delete json[propVersion];
out.push({name, json});
}
} catch (e) {
errs.push({filename: name, message: e.message});
}
if (input.files[readIndex]) {
reader.readAsText(input.files[readIndex++]);
return;
}
resolve({
files: out,
errors: errs,
jsons: out.map(({json}) => json),
});
};
reader.readAsText(input.files[readIndex++]);
})
.appendTo(document.body);
$iptAdd.click();
});
}
}
class DragReorderUiUtil {
@@ -4816,16 +4886,17 @@ class ComponentUiUtil {
// breaks when the number is negative, as we need to add a "=" to the front of the input before
// evaluating
// $ipt.change();
const nxt = component._state[prop] + delta;
const cur = isNaN(component._state[prop]) ? opts.fallbackOnNaN : component._state[prop];
const nxt = cur + delta;
if (!isValidValue(nxt)) return;
component._state[prop] = nxt;
$ipt.focus();
};
const $btnUp = $(`<button class="btn btn-default ui-ideco__btn-ticker bold no-select">+</button>`)
const $btnUp = $(`<button class="btn btn-default ui-ideco__btn-ticker p-0 bold no-select">+</button>`)
.click(() => handleClick(1));
const $btnDown = $(`<button class="btn btn-default ui-ideco__btn-ticker bold no-select">\u2012</button>`)
const $btnDown = $(`<button class="btn btn-default ui-ideco__btn-ticker p-0 bold no-select">\u2012</button>`)
.click(() => handleClick(-1));
return $$`<div class="ui-ideco__wrp ui-ideco__wrp--${side} ve-flex-vh-center ve-flex-col">

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.206.0"/* 5ETOOLS_VERSION__CLOSE */;
globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.206.1"/* 5ETOOLS_VERSION__CLOSE */;
globalThis.DEPLOYED_IMG_ROOT = undefined;
// for the roll20 script to set
globalThis.IS_VTT = false;
@@ -1332,26 +1332,23 @@ globalThis.MiscUtil = class {
$iptTemp.remove();
}
if (navigator && navigator.permissions) {
try {
const access = await navigator.permissions.query({name: "clipboard-write"});
if (access.state === "granted" || access.state === "prompt") {
await navigator.clipboard.writeText(text);
} else doCompatibilityCopy();
} catch (e) { doCompatibilityCopy(); }
} else doCompatibilityCopy();
try {
await navigator.clipboard.writeText(text);
} catch (e) {
doCompatibilityCopy();
}
}
static async pCopyBlobToClipboard (blob) {
if (!navigator?.permissions) {
JqueryUtil.doToast({type: "danger", content: `Could not access clipboard!`});
return false;
}
const access = await navigator.permissions.query({name: "clipboard-write"});
if (!["granted", "prompt"].includes(access.state)) {
JqueryUtil.doToast({type: "danger", content: `Could not access clipboard!`});
return false;
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem#browser_compatibility
// TODO(Future) remove when Firefox moves feature from Nightly -> Main
if (typeof ClipboardItem === "undefined") {
JqueryUtil.doToast({
type: "danger",
content: `Could not access clipboard! If you are on Firefox, visit <code>about:config</code> and enable </code><code>dom.events.asyncClipboard.clipboardItem</code>.`,
isAutoHide: false,
});
return;
}
try {
@@ -1379,7 +1376,7 @@ globalThis.MiscUtil = class {
}
static get (object, ...path) {
if (object == null) return null;
if (object == null) return object;
for (let i = 0; i < path.length; ++i) {
object = object[path[i]];
if (object == null) return object;
@@ -1388,7 +1385,7 @@ globalThis.MiscUtil = class {
}
static set (object, ...pathAndVal) {
if (object == null) return null;
if (object == null) return object;
const val = pathAndVal.pop();
if (!pathAndVal.length) return null;
@@ -2095,6 +2092,16 @@ globalThis.MiscUtil = class {
if (a != null && b == null) return false;
return a === b;
}
static getDatUrl (blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.onabort = () => reject(new Error("Read aborted"));
reader.readAsDataURL(blob);
});
}
};
// EVENT HANDLERS ======================================================================================================
@@ -3853,71 +3860,6 @@ globalThis.DataUtil = {
a.dispatchEvent(new MouseEvent("click", {bubbles: true, cancelable: true, view: window}));
},
/** Always returns an array of files, even in "single" mode. */
pUserUpload (
{
isMultiple = false,
expectedFileTypes = null,
propVersion = "siteVersion",
} = {},
) {
return new Promise(resolve => {
const $iptAdd = $(`<input type="file" ${isMultiple ? "multiple" : ""} class="ve-hidden" accept=".json">`)
.on("change", (evt) => {
const input = evt.target;
const reader = new FileReader();
let readIndex = 0;
const out = [];
const errs = [];
reader.onload = async () => {
const name = input.files[readIndex - 1].name;
const text = reader.result;
try {
const json = JSON.parse(text);
const isSkipFile = expectedFileTypes != null
&& json.fileType
&& !expectedFileTypes.includes(json.fileType)
&& !(await InputUiUtil.pGetUserBoolean({
textYes: "Yes",
textNo: "Cancel",
title: "File Type Mismatch",
htmlDescription: `The file "${name}" has the type "${json.fileType}" when the expected file type was "${expectedFileTypes.join("/")}".<br>Are you sure you want to upload this file?`,
}));
if (!isSkipFile) {
delete json.fileType;
delete json[propVersion];
out.push({name, json});
}
} catch (e) {
errs.push({filename: name, message: e.message});
}
if (input.files[readIndex]) {
reader.readAsText(input.files[readIndex++]);
return;
}
resolve({
files: out,
errors: errs,
jsons: out.map(({json}) => json),
});
};
reader.readAsText(input.files[readIndex++]);
})
.appendTo(document.body);
$iptAdd.click();
});
},
doHandleFileLoadErrorsGeneric (errors) {
if (!errors) return;
errors.forEach(err => {
@@ -4184,6 +4126,8 @@ globalThis.DataUtil = {
],
copyApplier: class {
static _WALKER = null;
// convert everything to arrays
static _normaliseMods (obj) {
Object.entries(obj._mod).forEach(([k, v]) => {
@@ -4573,12 +4517,32 @@ globalThis.DataUtil = {
static _doMod_scalarAddHit ({copyTo, copyFrom, modInfo, msgPtFailed, prop}) {
if (!copyTo[prop]) return;
copyTo[prop] = JSON.parse(JSON.stringify(copyTo[prop]).replace(/{@hit ([-+]?\d+)}/g, (m0, m1) => `{@hit ${Number(m1) + modInfo.scalar}}`));
const re = /{@hit ([-+]?\d+)}/g;
copyTo[prop] = this._WALKER.walk(
copyTo[prop],
{
string: (str) => {
return str
.replace(re, (m0, m1) => `{@hit ${Number(m1) + modInfo.scalar}}`);
},
},
);
}
static _doMod_scalarAddDc ({copyTo, copyFrom, modInfo, msgPtFailed, prop}) {
if (!copyTo[prop]) return;
copyTo[prop] = JSON.parse(JSON.stringify(copyTo[prop]).replace(/{@dc (\d+)(?:\|[^}]+)?}/g, (m0, m1) => `{@dc ${Number(m1) + modInfo.scalar}}`));
const re = /{@dc (\d+)(?:\|[^}]+)?}/g;
copyTo[prop] = this._WALKER.walk(
copyTo[prop],
{
string: (str) => {
return str
.replace(re, (m0, m1) => `{@dc ${Number(m1) + modInfo.scalar}}`);
},
},
);
}
static _doMod_maxSize ({copyTo, copyFrom, modInfo, msgPtFailed}) {
@@ -4677,6 +4641,8 @@ globalThis.DataUtil = {
}
static getCopy (impl, copyFrom, copyTo, templateData, {isExternalApplicationKeepCopy = false, isExternalApplicationIdentityOnly = false} = {}) {
this._WALKER ||= MiscUtil.getWalker();
if (isExternalApplicationKeepCopy) copyTo.__copy = MiscUtil.copyFast(copyFrom);
const msgPtFailed = `Failed to apply _copy to "${copyTo.name}" ("${copyTo.source}").`;
@@ -7764,6 +7730,12 @@ globalThis.EditorUtil = {
},
};
globalThis.BrowserUtil = class {
static isFirefox () {
return navigator.userAgent.includes("Firefox");
}
};
// MISC WEBPAGE ONLOADS ================================================================================================
if (!IS_VTT && typeof window !== "undefined") {
window.addEventListener("load", () => {