mirror of
https://github.com/Kornstalx/5etools-mirror-2.github.io.git
synced 2025-10-28 20:45:35 -05:00
v1.206.0
This commit is contained in:
104
js/utils.js
104
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.205.0"/* 5ETOOLS_VERSION__CLOSE */;
|
||||
globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.206.0"/* 5ETOOLS_VERSION__CLOSE */;
|
||||
globalThis.DEPLOYED_IMG_ROOT = undefined;
|
||||
// for the roll20 script to set
|
||||
globalThis.IS_VTT = false;
|
||||
@@ -1342,6 +1342,34 @@ globalThis.MiscUtil = class {
|
||||
} else 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;
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({[blob.type]: blob}),
|
||||
]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (e.message.includes("Document is not focused")) {
|
||||
JqueryUtil.doToast({type: "danger", content: `Please focus the window first!`});
|
||||
return false;
|
||||
}
|
||||
|
||||
JqueryUtil.doToast({type: "danger", content: `Failed to copy! ${VeCt.STR_SEE_CONSOLE}`});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
static checkProperty (object, ...path) {
|
||||
for (let i = 0; i < path.length; ++i) {
|
||||
object = object[path[i]];
|
||||
@@ -2428,6 +2456,9 @@ globalThis.ContextUtil = {
|
||||
const result = await this.fnAction(evt, {userData: menu.userData});
|
||||
if (menu.resolveResult_) menu.resolveResult_(result);
|
||||
})
|
||||
.on("mousedown", evt => {
|
||||
evt.preventDefault();
|
||||
})
|
||||
.keydown(evt => {
|
||||
if (evt.key !== "Enter") return;
|
||||
$btnAction.click();
|
||||
@@ -2451,6 +2482,9 @@ globalThis.ContextUtil = {
|
||||
|
||||
const result = await this.fnActionAlt(evt, {userData: menu.userData});
|
||||
if (menu.resolveResult_) menu.resolveResult_(result);
|
||||
})
|
||||
.on("mousedown", evt => {
|
||||
evt.preventDefault();
|
||||
});
|
||||
if (this.titleAlt) $btnActionAlt.title(this.titleAlt);
|
||||
|
||||
@@ -2585,6 +2619,9 @@ globalThis.ContextUtil = {
|
||||
);
|
||||
|
||||
menu.close();
|
||||
})
|
||||
.on("mousedown", evt => {
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -3414,6 +3451,26 @@ globalThis.SortUtil = {
|
||||
},
|
||||
};
|
||||
|
||||
globalThis.MultiSourceUtil = class {
|
||||
static getIndexKey (prop, ent) {
|
||||
switch (prop) {
|
||||
case "class":
|
||||
case "classFluff":
|
||||
return (ent.name || "").toLowerCase().split(" ").at(-1);
|
||||
case "subclass":
|
||||
case "subclassFluff":
|
||||
return (ent.className || "").toLowerCase().split(" ").at(-1);
|
||||
default:
|
||||
return ent.source;
|
||||
}
|
||||
}
|
||||
|
||||
static isEntityIndexKeyMatch (indexKey, prop, ent) {
|
||||
if (indexKey == null) return true;
|
||||
return indexKey === MultiSourceUtil.getIndexKey(prop, ent);
|
||||
}
|
||||
};
|
||||
|
||||
// JSON LOADING ========================================================================================================
|
||||
class _DataUtilPropConfig {
|
||||
static _MERGE_REQUIRES_PRESERVE = {};
|
||||
@@ -3452,7 +3509,7 @@ class _DataUtilPropConfigMultiSource extends _DataUtilPropConfig {
|
||||
|
||||
static async pLoadAll () {
|
||||
const json = await this.loadJSON();
|
||||
return json[this._PROP];
|
||||
return json[this._PROP] || [];
|
||||
}
|
||||
|
||||
static async loadJSON () { return this._loadJSON({isUnmerged: false}); }
|
||||
@@ -3462,7 +3519,7 @@ class _DataUtilPropConfigMultiSource extends _DataUtilPropConfig {
|
||||
const index = await this.pLoadIndex();
|
||||
|
||||
const allData = await Object.entries(index)
|
||||
.pMap(async ([source, file]) => this._pLoadSourceEntities({source, isUnmerged, file}));
|
||||
.pMap(async ([indexKey, file]) => this._pLoadSourceEntities({indexKey, isUnmerged, file}));
|
||||
|
||||
return {[this._PROP]: allData.flat()};
|
||||
}
|
||||
@@ -3473,16 +3530,16 @@ class _DataUtilPropConfigMultiSource extends _DataUtilPropConfig {
|
||||
const file = index[source];
|
||||
if (!file) return null;
|
||||
|
||||
return {[this._PROP]: await this._pLoadSourceEntities({source, file})};
|
||||
return {[this._PROP]: await this._pLoadSourceEntities({indexKey: source, file})};
|
||||
}
|
||||
|
||||
static async _pLoadSourceEntities ({source, isUnmerged = false, file}) {
|
||||
static async _pLoadSourceEntities ({indexKey = null, isUnmerged = false, file}) {
|
||||
await this._pInitPreData();
|
||||
|
||||
const fnLoad = isUnmerged ? DataUtil.loadRawJSON.bind(DataUtil) : DataUtil.loadJSON.bind(DataUtil);
|
||||
|
||||
let data = await fnLoad(`${Renderer.get().baseUrl}data/${this._DIR}/${file}`);
|
||||
data = data[this._PROP].filter(it => it.source === source);
|
||||
data = (data[this._PROP] || []).filter(MultiSourceUtil.isEntityIndexKeyMatch.bind(this, indexKey, this._PROP));
|
||||
|
||||
if (!this._IS_MUT_ENTITIES) return data;
|
||||
|
||||
@@ -3783,12 +3840,17 @@ globalThis.DataUtil = {
|
||||
},
|
||||
|
||||
_userDownload (filename, data, mimeType) {
|
||||
const a = document.createElement("a");
|
||||
const t = new Blob([data], {type: mimeType});
|
||||
a.href = window.URL.createObjectURL(t);
|
||||
const dataUrl = window.URL.createObjectURL(t);
|
||||
DataUtil.userDownloadDataUrl(filename, dataUrl);
|
||||
setTimeout(() => window.URL.revokeObjectURL(dataUrl), 100);
|
||||
},
|
||||
|
||||
userDownloadDataUrl (filename, dataUrl) {
|
||||
const a = document.createElement("a");
|
||||
a.href = dataUrl;
|
||||
a.download = filename;
|
||||
a.dispatchEvent(new MouseEvent("click", {bubbles: true, cancelable: true, view: window}));
|
||||
setTimeout(() => window.URL.revokeObjectURL(a.href), 100);
|
||||
},
|
||||
|
||||
/** Always returns an array of files, even in "single" mode. */
|
||||
@@ -3901,7 +3963,9 @@ globalThis.DataUtil = {
|
||||
"spell": "spells",
|
||||
"spellFluff": "spells",
|
||||
"class": "class",
|
||||
"classFluff": "class",
|
||||
"subclass": "class",
|
||||
"subclassFluff": "class",
|
||||
"classFeature": "class",
|
||||
"subclassFeature": "class",
|
||||
},
|
||||
@@ -3929,7 +3993,9 @@ globalThis.DataUtil = {
|
||||
|
||||
// region Multi-source
|
||||
case "class":
|
||||
case "classFluff":
|
||||
case "subclass":
|
||||
case "subclassFluff":
|
||||
case "classFeature":
|
||||
case "subclassFeature": {
|
||||
const baseUrlPart = `${Renderer.get().baseUrl}data/${DataUtil._MULTI_SOURCE_PROP_TO_DIR[prop]}`;
|
||||
@@ -5623,7 +5689,6 @@ globalThis.DataUtil = {
|
||||
for (const r of recipes) {
|
||||
const fluff = await Renderer.utils.pGetFluff({
|
||||
entity: r,
|
||||
fnGetFluffData: DataUtil.recipeFluff.loadJSON.bind(DataUtil.recipeFluff),
|
||||
fluffProp: "recipeFluff",
|
||||
});
|
||||
|
||||
@@ -5839,8 +5904,25 @@ globalThis.DataUtil = {
|
||||
// endregion
|
||||
},
|
||||
|
||||
subclass: class extends _DataUtilPropConfig {
|
||||
classFluff: class extends _DataUtilPropConfigMultiSource {
|
||||
static _PAGE = UrlUtil.PG_CLASSES;
|
||||
static _DIR = "class";
|
||||
static _PROP = "classFluff";
|
||||
},
|
||||
|
||||
subclass: class extends _DataUtilPropConfigCustom {
|
||||
static _PAGE = "subclass";
|
||||
static _PROP = "subclassFluff";
|
||||
|
||||
static async loadJSON () {
|
||||
return DataUtil.class.loadJSON();
|
||||
}
|
||||
},
|
||||
|
||||
subclassFluff: class extends _DataUtilPropConfigMultiSource {
|
||||
static _PAGE = "subclassFluff";
|
||||
static _DIR = "class";
|
||||
static _PROP = "subclassFluff";
|
||||
},
|
||||
|
||||
deity: class extends _DataUtilPropConfigSingleSource {
|
||||
|
||||
Reference in New Issue
Block a user