mirror of
https://github.com/Kornstalx/5etools-mirror-2.github.io.git
synced 2025-10-28 20:45:35 -05:00
v1.206.1
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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">→</span>
|
||||
${$dispTableEntry}
|
||||
${$dispTableRoll}
|
||||
|
||||
@@ -50,6 +50,7 @@ class MapsUtil {
|
||||
? {
|
||||
[head.id]: {
|
||||
id: head.id,
|
||||
name: head.name,
|
||||
source: head.source,
|
||||
prop,
|
||||
parentSource: head.parentSource,
|
||||
|
||||
38
js/maps.js
38
js/maps.js
@@ -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()); }
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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} = {}) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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})],
|
||||
});
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
146
js/utils.js
146
js/utils.js
@@ -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", () => {
|
||||
|
||||
Reference in New Issue
Block a user