Files
srhtnin.github.io/Scripts/Garden/AdminPlantEditor.js
T

864 lines
15 KiB
JavaScript

let AdminPlantCatalogue = {};
let AdminPlantEditingKey = null;
let AdminPlantSavePending = false;
async function StartAdminPlantEditor() {
try {
const Status =
await GetAdminStatus();
if (
Status.Success !== true ||
Status.IsAdmin !== true
) {
SetAdminPlantMessage(
"Admin access required."
);
return;
}
BindAdminPlantEditor();
await LoadAdminPlants();
} catch (Error) {
console.error(
"Couldn't start Plant editor:",
Error
);
SetAdminPlantMessage(
Error.message ??
"Couldn't load the Plant editor."
);
}
}
function BindAdminPlantEditor() {
document.getElementById(
"AdminPlantSelect"
).addEventListener(
"change",
Event => {
const PlantKey =
Event.target.value;
if (PlantKey === "") {
StartNewAdminPlant();
return;
}
LoadAdminPlantIntoForm(
PlantKey
);
}
);
document.getElementById(
"AdminPlantNewButton"
).addEventListener(
"click",
StartNewAdminPlant
);
document.getElementById(
"AdminPlantForm"
).addEventListener(
"submit",
SaveAdminPlant
);
for (
const ElementId
of [
"AdminPlantId",
"AdminPlantKey",
"AdminPlantName",
"AdminPlantGrowthTime",
"AdminPlantShopPlant",
"AdminPlantBaseCost"
]
) {
document.getElementById(
ElementId
).addEventListener(
"input",
RenderAdminPlantPreview
);
document.getElementById(
ElementId
).addEventListener(
"change",
RenderAdminPlantPreview
);
}
document.getElementById(
"AdminPlantShopPlant"
).addEventListener(
"change",
UpdateAdminPlantShopFields
);
}
async function AdminPlantRequest(
Action,
Data = {}
) {
const SaveKey =
localStorage.getItem(
SaveKeyName
);
if (
typeof SaveKey !== "string" ||
!/^[0-9A-Fa-f]{64}$/.test(
SaveKey
)
) {
throw new Error(
"A valid Account Key is required."
);
}
const Response = await fetch(
ApiUrl +
"/AdminPlants.php",
{
method: "POST",
headers: {
"Content-Type":
"application/json"
},
body: JSON.stringify({
SaveKey: SaveKey,
Action: Action,
...Data
})
}
);
let Result;
try {
Result = await Response.json();
} catch (Error) {
throw new Error(
"Plant admin API returned invalid JSON."
);
}
if (
!Response.ok ||
Result.Success !== true
) {
throw new Error(
Result.Error ??
"Plant admin request failed."
);
}
return Result;
}
async function LoadAdminPlants(
PreferredPlantKey = null
) {
SetAdminPlantMessage(
"Loading plants..."
);
const Result =
await AdminPlantRequest(
"List"
);
AdminPlantCatalogue =
Result.Plants ?? {};
Plants =
AdminPlantCatalogue;
RenderAdminPlantSelect();
const Keys =
GetSortedAdminPlantKeys();
const PlantKey =
PreferredPlantKey !== null &&
AdminPlantCatalogue[
PreferredPlantKey
] !== undefined
? PreferredPlantKey
: Keys[0] ?? null;
if (PlantKey === null) {
StartNewAdminPlant();
} else {
LoadAdminPlantIntoForm(
PlantKey
);
}
SetAdminPlantMessage(
Keys.length === 1
? "1 plant loaded."
: Keys.length +
" plants loaded."
);
}
function GetSortedAdminPlantKeys() {
return Object.keys(
AdminPlantCatalogue
).sort(
(A, B) => {
const PlantA =
AdminPlantCatalogue[A];
const PlantB =
AdminPlantCatalogue[B];
return (
Number(PlantA.Id) -
Number(PlantB.Id) ||
A.localeCompare(B)
);
}
);
}
function RenderAdminPlantSelect() {
const Select =
document.getElementById(
"AdminPlantSelect"
);
Select.replaceChildren();
for (
const PlantKey
of GetSortedAdminPlantKeys()
) {
const Plant =
AdminPlantCatalogue[
PlantKey
];
const Option =
document.createElement(
"option"
);
Option.value =
PlantKey;
Option.textContent =
String(
Plant.Id
).padStart(
3,
"0"
) +
" — " +
Plant.Name +
(
Plant.Archived === true
? " (archived)"
: ""
);
Select.appendChild(
Option
);
}
}
function LoadAdminPlantIntoForm(
PlantKey
) {
const Plant =
AdminPlantCatalogue[
PlantKey
];
if (Plant === undefined) {
return;
}
AdminPlantEditingKey =
PlantKey;
document.getElementById(
"AdminPlantSelect"
).value = PlantKey;
SetAdminPlantField(
"AdminPlantId",
Plant.Id
);
SetAdminPlantField(
"AdminPlantKey",
PlantKey
);
SetAdminPlantField(
"AdminPlantName",
Plant.Name ?? ""
);
SetAdminPlantField(
"AdminPlantDescription",
Plant.Description ?? ""
);
SetAdminPlantField(
"AdminPlantTags",
Array.isArray(
Plant.Tags
)
? Plant.Tags.join(", ")
: ""
);
SetAdminPlantField(
"AdminPlantGrowthTime",
Number(
Plant.GrowthTime ?? 0
)
);
document.getElementById(
"AdminPlantShopPlant"
).checked =
Plant.Shop?.ShopPlant === true;
SetAdminPlantField(
"AdminPlantBaseCost",
Plant.Shop?.BaseCost ?? ""
);
SetAdminPlantField(
"AdminPlantEffects",
JSON.stringify(
Plant.Effects ?? {},
null,
4
)
);
document.getElementById(
"AdminPlantId"
).readOnly = true;
document.getElementById(
"AdminPlantKey"
).readOnly = true;
document.getElementById(
"AdminPlantArchiveNote"
).hidden =
Plant.Archived !== true;
UpdateAdminPlantShopFields();
RenderAdminPlantPreview();
}
function StartNewAdminPlant() {
AdminPlantEditingKey = null;
document.getElementById(
"AdminPlantSelect"
).value = "";
SetAdminPlantField(
"AdminPlantId",
GetNextAdminPlantId()
);
SetAdminPlantField(
"AdminPlantKey",
""
);
SetAdminPlantField(
"AdminPlantName",
""
);
SetAdminPlantField(
"AdminPlantDescription",
""
);
SetAdminPlantField(
"AdminPlantTags",
""
);
SetAdminPlantField(
"AdminPlantGrowthTime",
3600000
);
document.getElementById(
"AdminPlantShopPlant"
).checked = false;
SetAdminPlantField(
"AdminPlantBaseCost",
""
);
SetAdminPlantField(
"AdminPlantEffects",
"{}"
);
document.getElementById(
"AdminPlantId"
).readOnly = false;
document.getElementById(
"AdminPlantKey"
).readOnly = false;
document.getElementById(
"AdminPlantArchiveNote"
).hidden = true;
UpdateAdminPlantShopFields();
RenderAdminPlantPreview();
SetAdminPlantMessage(
"Creating a new plant."
);
}
function GetNextAdminPlantId() {
return Math.max(
0,
...Object.values(
AdminPlantCatalogue
).map(
Plant =>
Number(
Plant.Id ?? 0
)
)
) + 1;
}
function SetAdminPlantField(
ElementId,
Value
) {
document.getElementById(
ElementId
).value =
Value ?? "";
}
function UpdateAdminPlantShopFields() {
const ShopPlant =
document.getElementById(
"AdminPlantShopPlant"
).checked;
const BaseCost =
document.getElementById(
"AdminPlantBaseCost"
);
BaseCost.disabled =
!ShopPlant;
BaseCost.required =
ShopPlant;
}
function ParseAdminPlantTags() {
const Value =
document.getElementById(
"AdminPlantTags"
).value;
return [
...new Set(
Value
.split(",")
.map(
Tag =>
Tag.trim()
)
.filter(
Tag =>
Tag.length > 0
)
)
];
}
function ParseAdminPlantEffects() {
const Value =
document.getElementById(
"AdminPlantEffects"
).value.trim();
if (Value === "") {
return {};
}
const Effects =
JSON.parse(Value);
if (
Effects === null ||
typeof Effects !== "object" ||
Array.isArray(Effects)
) {
throw new Error(
"Effects JSON must be an object."
);
}
return Effects;
}
function GetAdminPlantFormData() {
const ShopPlant =
document.getElementById(
"AdminPlantShopPlant"
).checked;
const BaseCostValue =
document.getElementById(
"AdminPlantBaseCost"
).value;
return {
Id: Number(
document.getElementById(
"AdminPlantId"
).value
),
PlantKey:
document.getElementById(
"AdminPlantKey"
).value.trim(),
Name:
document.getElementById(
"AdminPlantName"
).value.trim(),
Description:
document.getElementById(
"AdminPlantDescription"
).value.trim(),
Tags:
ParseAdminPlantTags(),
GrowthTime: Number(
document.getElementById(
"AdminPlantGrowthTime"
).value
),
Effects:
ParseAdminPlantEffects(),
ShopPlant: ShopPlant,
BaseCost:
ShopPlant
? Number(BaseCostValue)
: null,
IsNew:
AdminPlantEditingKey ===
null
};
}
async function SaveAdminPlant(
Event
) {
Event.preventDefault();
if (AdminPlantSavePending) {
return;
}
const Form =
document.getElementById(
"AdminPlantForm"
);
if (!Form.reportValidity()) {
return;
}
let Plant;
try {
Plant =
GetAdminPlantFormData();
} catch (Error) {
SetAdminPlantMessage(
Error.message
);
return;
}
AdminPlantSavePending = true;
const SaveButton =
document.getElementById(
"AdminPlantSaveButton"
);
SaveButton.disabled = true;
SaveButton.textContent =
"Saving...";
SetAdminPlantMessage(
"Saving plant..."
);
try {
const Result =
await AdminPlantRequest(
"Save",
{
Plant: Plant
}
);
localStorage.removeItem(
"SarahtoninGardenContent"
);
await LoadAdminPlants(
Result.PlantKey ??
Plant.PlantKey
);
SetAdminPlantMessage(
"Plant saved."
);
} catch (Error) {
console.error(
"Couldn't save plant:",
Error
);
SetAdminPlantMessage(
Error.message ??
"Couldn't save plant."
);
} finally {
AdminPlantSavePending = false;
SaveButton.disabled = false;
SaveButton.textContent =
"Save plant";
}
}
function RenderAdminPlantPreview() {
const PlantKey =
document.getElementById(
"AdminPlantKey"
).value.trim();
const Name =
document.getElementById(
"AdminPlantName"
).value.trim() ||
"New plant";
const Tile =
document.getElementById(
"AdminPlantPreviewTile"
);
Tile.replaceChildren();
const Images =
PlantImages[
PlantKey
] ?? [];
if (
Array.isArray(Images) &&
Images.length > 0
) {
const Image =
document.createElement(
"img"
);
Image.className =
"PlantSprite";
Image.src =
Images[
Images.length - 1
];
Image.alt = Name;
Tile.appendChild(
Image
);
} else {
const Missing =
document.createElement(
"span"
);
Missing.className =
"AdminPlantPreviewMissing";
Missing.textContent =
PlantKey === ""
? "No key"
: "No sprite";
Tile.appendChild(
Missing
);
}
document.getElementById(
"AdminPlantPreviewName"
).textContent = Name;
document.getElementById(
"AdminPlantPreviewKey"
).textContent =
PlantKey === ""
? "Plant Key not set"
: PlantKey;
const GrowthTime = Number(
document.getElementById(
"AdminPlantGrowthTime"
).value
);
document.getElementById(
"AdminPlantGrowthTimeHint"
).textContent =
Number.isFinite(GrowthTime)
? FormatAdminDuration(
GrowthTime
)
: "";
}
function FormatAdminDuration(
Milliseconds
) {
Milliseconds = Math.max(
0,
Number(Milliseconds)
);
if (Milliseconds < 1000) {
return Milliseconds + "ms";
}
const TotalSeconds =
Math.round(
Milliseconds /
1000
);
const Hours =
Math.floor(
TotalSeconds /
3600
);
const Minutes =
Math.floor(
(
TotalSeconds %
3600
) /
60
);
const Seconds =
TotalSeconds % 60;
const Parts = [];
if (Hours > 0) {
Parts.push(
Hours + "h"
);
}
if (Minutes > 0) {
Parts.push(
Minutes + "m"
);
}
if (
Seconds > 0 ||
Parts.length === 0
) {
Parts.push(
Seconds + "s"
);
}
return Parts.join(" ");
}
function SetAdminPlantMessage(
Message
) {
const Element =
document.getElementById(
"AdminPlantMessage"
);
if (Element !== null) {
Element.textContent =
Message;
}
}
document.addEventListener(
"DOMContentLoaded",
StartAdminPlantEditor
);