mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Split client into frontend website and server
This commit is contained in:
110
frontend/website/plugins/databasemanager/src/airuniteditor.ts
Normal file
110
frontend/website/plugins/databasemanager/src/airuniteditor.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { LoadoutBlueprint, UnitBlueprint } from "interfaces";
|
||||
import { UnitEditor } from "./uniteditor";
|
||||
import { LoadoutEditor } from "./loadouteditor";
|
||||
import { addCheckboxInput, addDropdownInput, addLoadoutsScroll, addNewElementInput, addStringInput } from "./utils";
|
||||
|
||||
/** Database editor for Air Units, both Aircraft and Helicopter since they are identical in terms of datbase entries.
|
||||
*
|
||||
*/
|
||||
export class AirUnitEditor extends UnitEditor {
|
||||
|
||||
#loadoutEditor: LoadoutEditor | null = null;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
super(contentDiv1, contentDiv2, contentDiv3);
|
||||
|
||||
/* The loadout editor allows to edit the loadout (who could have thought eh?) */
|
||||
this.#loadoutEditor = new LoadoutEditor(this.contentDiv3);
|
||||
|
||||
/* Refresh the loadout editor if needed */
|
||||
this.contentDiv3.addEventListener("refresh", () => {
|
||||
if (this.visible)
|
||||
this.#loadoutEditor?.show();
|
||||
});
|
||||
}
|
||||
|
||||
/** Sets a unit blueprint as the currently active one
|
||||
*
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint: UnitBlueprint) {
|
||||
this.blueprint = blueprint;
|
||||
|
||||
if (this.blueprint !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Unit properties";
|
||||
this.contentDiv2.appendChild(title);
|
||||
|
||||
addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => { blueprint.name = value; }, true);
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => { blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => { blueprint.shortLabel = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
addStringInput(this.contentDiv2, "Filename", blueprint.filename ?? "", "text", (value: string) => { blueprint.filename = value; });
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost) ?? "", "number", (value: string) => { blueprint.cost = parseFloat(value); });
|
||||
addCheckboxInput(this.contentDiv2, "Can target point", blueprint.canTargetPoint ?? false, (value: boolean) => {blueprint.canTargetPoint = value;})
|
||||
addStringInput(this.contentDiv2, "Description", blueprint.description ?? "", "text", (value: string) => {blueprint.description = value; });
|
||||
addStringInput(this.contentDiv2, "Tags", blueprint.tags ?? "", "text", (value: string) => {blueprint.tags = value; });
|
||||
|
||||
/* Add a scrollable list of loadouts that the user can edit */
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Loadouts";
|
||||
this.contentDiv2.appendChild(title);
|
||||
addLoadoutsScroll(this.contentDiv2, blueprint.loadouts ?? [], (loadout: LoadoutBlueprint) => {
|
||||
this.#loadoutEditor?.setLoadout(loadout);
|
||||
this.#loadoutEditor?.show();
|
||||
});
|
||||
addNewElementInput(this.contentDiv2, (ev: MouseEvent, input: HTMLInputElement) => { this.addLoadout(input.value); });
|
||||
|
||||
this.#loadoutEditor?.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty blueprint
|
||||
*
|
||||
* @param key Blueprint key
|
||||
*/
|
||||
addBlueprint(key: string) {
|
||||
if (this.database != null) {
|
||||
this.database.blueprints[key] = {
|
||||
name: key,
|
||||
coalition: "",
|
||||
label: "",
|
||||
shortLabel: "",
|
||||
era: "",
|
||||
loadouts: [],
|
||||
enabled: true
|
||||
}
|
||||
this.show();
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty loadout to the currently active blueprint
|
||||
*
|
||||
* @param loadoutName The name of the new loadout
|
||||
*/
|
||||
addLoadout(loadoutName: string) {
|
||||
if (loadoutName && this.blueprint !== null) {
|
||||
this.blueprint.loadouts?.push({
|
||||
name: loadoutName,
|
||||
code: "",
|
||||
fuel: 1,
|
||||
items: [],
|
||||
roles: [],
|
||||
enabled: true
|
||||
})
|
||||
this.setBlueprint(this.blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
super.hide();
|
||||
this.#loadoutEditor?.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
import { OlympusPlugin, UnitBlueprint } from "interfaces";
|
||||
import { AirUnitEditor } from "./airuniteditor";
|
||||
import { OlympusApp } from "olympusapp";
|
||||
import { GroundUnitEditor } from "./grounduniteditor";
|
||||
import { PrimaryToolbar } from "toolbars/primarytoolbar";
|
||||
import { NavyUnitEditor } from "./navyuniteditor";
|
||||
|
||||
/** Database Manager
|
||||
*
|
||||
* This database provides a user interface to allow easier and convenient unit databases manipulation. It allows to edit all the fields of the units databases, save them
|
||||
* on the server, and restore the defaults.
|
||||
*
|
||||
* TODO:
|
||||
* Add ability to manage liveries
|
||||
*
|
||||
*/
|
||||
|
||||
export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
#app!: OlympusApp;
|
||||
|
||||
#element: HTMLElement;
|
||||
#mainContentContainer: HTMLElement;
|
||||
#contentDiv1: HTMLElement;
|
||||
#contentDiv2: HTMLElement;
|
||||
#contentDiv3: HTMLElement;
|
||||
|
||||
/* Upper tab buttons */
|
||||
#button1: HTMLButtonElement;
|
||||
#button2: HTMLButtonElement;
|
||||
#button3: HTMLButtonElement;
|
||||
#button4: HTMLButtonElement;
|
||||
|
||||
/* Lower operation buttons */
|
||||
#button5: HTMLButtonElement;
|
||||
#button6: HTMLButtonElement;
|
||||
#button7: HTMLButtonElement;
|
||||
#button8: HTMLButtonElement;
|
||||
#button9: HTMLButtonElement;
|
||||
|
||||
/* Database editors */
|
||||
#aircraftEditor: AirUnitEditor;
|
||||
#helicopterEditor: AirUnitEditor;
|
||||
#groundUnitEditor: GroundUnitEditor;
|
||||
#navyUnitEditor: NavyUnitEditor;
|
||||
|
||||
constructor() {
|
||||
/* Create main HTML element */
|
||||
this.#element = document.createElement("div");
|
||||
this.#element.id = "database-manager-panel";
|
||||
this.#element.oncontextmenu = () => { return false; }
|
||||
this.#element.classList.add("ol-dialog");
|
||||
document.body.appendChild(this.#element);
|
||||
|
||||
/* Start hidden */
|
||||
this.toggle(false);
|
||||
|
||||
/* Create the top tab buttons container and buttons */
|
||||
let topButtonContainer = document.createElement("div");
|
||||
|
||||
this.#button1 = document.createElement("button");
|
||||
this.#button1.classList.add("tab-button");
|
||||
this.#button1.textContent = "Aircraft database";
|
||||
this.#button1.onclick = () => { this.#hideAll(); this.#aircraftEditor.show(); this.#button1.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button1);
|
||||
|
||||
this.#button2 = document.createElement("button");
|
||||
this.#button2.classList.add("tab-button");
|
||||
this.#button2.textContent = "Helicopter database";
|
||||
this.#button2.onclick = () => { this.#hideAll(); this.#helicopterEditor.show(); this.#button2.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button2);
|
||||
|
||||
this.#button3 = document.createElement("button");
|
||||
this.#button3.classList.add("tab-button");
|
||||
this.#button3.textContent = "Ground Unit database";
|
||||
this.#button3.onclick = () => { this.#hideAll(); this.#groundUnitEditor.show(); this.#button3.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button3);
|
||||
|
||||
this.#button4 = document.createElement("button");
|
||||
this.#button4.classList.add("tab-button");
|
||||
this.#button4.textContent = "Navy Unit database";
|
||||
this.#button4.onclick = () => { this.#hideAll(); this.#navyUnitEditor.show(); this.#button4.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button4);
|
||||
|
||||
this.#element.appendChild(topButtonContainer);
|
||||
|
||||
/* Create the container for the database editor elements and the elements themselves */
|
||||
this.#mainContentContainer = document.createElement("div");
|
||||
this.#mainContentContainer.classList.add("dm-container");
|
||||
this.#element.appendChild(this.#mainContentContainer);
|
||||
|
||||
this.#contentDiv1 = document.createElement("div");
|
||||
this.#contentDiv1.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv1);
|
||||
|
||||
this.#contentDiv2 = document.createElement("div");
|
||||
this.#contentDiv2.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv2);
|
||||
|
||||
this.#contentDiv3 = document.createElement("div");
|
||||
this.#contentDiv3.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv3);
|
||||
|
||||
/* Create the database editors, which use the three divs created before */
|
||||
this.#aircraftEditor = new AirUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
this.#helicopterEditor = new AirUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
this.#groundUnitEditor = new GroundUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
this.#navyUnitEditor = new NavyUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
|
||||
/* Create the bottom buttons container. These buttons allow to save, restore, reset, and discard the changes */
|
||||
let bottomButtonContainer = document.createElement("div");
|
||||
|
||||
this.#button5 = document.createElement("button");
|
||||
this.#button5.textContent = "Save";
|
||||
this.#button5.title = "Save the changes on the server"
|
||||
this.#button5.onclick = () => { this.#saveDatabases();};
|
||||
bottomButtonContainer.appendChild(this.#button5);
|
||||
|
||||
this.#button6 = document.createElement("button");
|
||||
this.#button6.textContent = "Discard";
|
||||
this.#button6.title = "Discard all changes and reload the database from the server";
|
||||
this.#button6.onclick = () => { this.#loadDatabases(); };
|
||||
bottomButtonContainer.appendChild(this.#button6);
|
||||
|
||||
this.#button7 = document.createElement("button");
|
||||
this.#button7.textContent = "Reset defaults";
|
||||
this.#button7.onclick = () => { this.#resetToDefaultDatabases(); };
|
||||
this.#button7.title = "Reset the databases to the default values";
|
||||
bottomButtonContainer.appendChild(this.#button7);
|
||||
|
||||
this.#button8 = document.createElement("button");
|
||||
this.#button8.textContent = "Restore previous";
|
||||
this.#button8.onclick = () => { this.#restoreToPreviousDatabases(); };
|
||||
this.#button8.title = "Restore the previously saved databases. Use this if you saved a database by mistake.";
|
||||
bottomButtonContainer.appendChild(this.#button8);
|
||||
|
||||
this.#button9 = document.createElement("button");
|
||||
this.#button9.textContent = "Close";
|
||||
this.#button9.title = "Close the Database Manager"
|
||||
this.#button9.onclick = () => { this.toggle(false); };
|
||||
bottomButtonContainer.appendChild(this.#button9);
|
||||
|
||||
this.#element.appendChild(bottomButtonContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The name of the plugin
|
||||
*/
|
||||
getName() {
|
||||
return "Database Control Plugin"
|
||||
}
|
||||
|
||||
/** Initialize the plugin
|
||||
*
|
||||
* @param app The OlympusApp singleton
|
||||
* @returns True if successfull
|
||||
*/
|
||||
initialize(app: any) {
|
||||
this.#app = app;
|
||||
|
||||
const contextManager = this.#app.getContextManager();
|
||||
contextManager.add( "databaseManager", {
|
||||
"allowUnitCopying": false,
|
||||
"allowUnitPasting": false,
|
||||
"useSpawnMenu": false,
|
||||
"useUnitControlPanel": false,
|
||||
"useUnitInfoPanel": false
|
||||
});
|
||||
|
||||
/* Load the databases and initialize the editors */
|
||||
this.#loadDatabases();
|
||||
|
||||
/* Add a button to the main Olympus App to allow the users to open the dialog */
|
||||
var mainButtonDiv = document.createElement("div");
|
||||
var mainButton = document.createElement("button");
|
||||
mainButton.textContent = "Database manager";
|
||||
mainButtonDiv.appendChild(mainButton);
|
||||
var toolbar: PrimaryToolbar = this.#app?.getToolbarsManager().get("primaryToolbar") as PrimaryToolbar;
|
||||
var elements = toolbar.getMainDropdown().getOptionElements();
|
||||
var arr = Array.prototype.slice.call(elements);
|
||||
arr.splice(arr.length - 3, 0, mainButtonDiv);
|
||||
toolbar.getMainDropdown().setOptionsElements(arr);
|
||||
mainButton.onclick = () => {
|
||||
toolbar.getMainDropdown().close();
|
||||
if (this.#app?.getMissionManager().getCommandModeOptions().commandMode === "Game master")
|
||||
this.toggle();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The main container element
|
||||
*/
|
||||
getElement() {
|
||||
return this.#element;
|
||||
}
|
||||
|
||||
/** Toggles the visibility of the dialog
|
||||
*
|
||||
* @param bool Force a specific visibility state
|
||||
*/
|
||||
toggle(bool?: boolean) {
|
||||
if (bool)
|
||||
this.getElement().classList.toggle("hide", !bool);
|
||||
else
|
||||
this.getElement().classList.toggle("hide");
|
||||
|
||||
if ( this.#app )
|
||||
this.#app.getContextManager().setContext( this.getElement().classList.contains("hide") ? "olympus" : "databaseManager" );
|
||||
}
|
||||
|
||||
/** Hide all the editors
|
||||
*
|
||||
*/
|
||||
#hideAll() {
|
||||
this.#aircraftEditor.hide();
|
||||
this.#helicopterEditor.hide();
|
||||
this.#groundUnitEditor.hide();
|
||||
this.#navyUnitEditor.hide();
|
||||
|
||||
this.#button1.classList.remove("selected");
|
||||
this.#button2.classList.remove("selected");
|
||||
this.#button3.classList.remove("selected");
|
||||
this.#button4.classList.remove("selected");
|
||||
}
|
||||
|
||||
/** Load the databases from the app to the editor. Note, this does not reload the databases from the server to the app
|
||||
*
|
||||
*/
|
||||
#loadDatabases() {
|
||||
var aircraftDatabase = this.#app?.getAircraftDatabase();
|
||||
if (aircraftDatabase != null)
|
||||
this.#aircraftEditor.setDatabase(aircraftDatabase);
|
||||
|
||||
var helicopterDatabase = this.#app?.getHelicopterDatabase();
|
||||
if (helicopterDatabase != null)
|
||||
this.#helicopterEditor.setDatabase(helicopterDatabase);
|
||||
|
||||
var groundUnitDatabase = this.#app?.getGroundUnitDatabase();
|
||||
if (groundUnitDatabase != null)
|
||||
this.#groundUnitEditor.setDatabase(groundUnitDatabase);
|
||||
|
||||
var navyUnitDatabase = this.#app?.getNavyUnitDatabase();
|
||||
if (navyUnitDatabase != null)
|
||||
this.#navyUnitEditor.setDatabase(navyUnitDatabase);
|
||||
|
||||
this.#hideAll();
|
||||
this.#aircraftEditor.show();
|
||||
this.#button1.classList.add("selected");
|
||||
}
|
||||
|
||||
/** Save the databases on the server and reloads it to apply the changes
|
||||
*
|
||||
*/
|
||||
#saveDatabases() {
|
||||
var aircraftDatabase = this.#aircraftEditor.getDatabase();
|
||||
if (aircraftDatabase){
|
||||
this.#uploadDatabase(aircraftDatabase, "aircraftdatabase", "Aircraft database", () => {
|
||||
var helicopterDatabase = this.#helicopterEditor.getDatabase();
|
||||
if (helicopterDatabase) {
|
||||
this.#uploadDatabase(helicopterDatabase, "helicopterDatabase", "Helicopter database", () => {
|
||||
var groundUnitDatabase = this.#groundUnitEditor.getDatabase();
|
||||
if (groundUnitDatabase) {
|
||||
this.#uploadDatabase(groundUnitDatabase, "groundUnitDatabase", "Ground Unit database", () => {
|
||||
var navyUnitDatabase = this.#navyUnitEditor.getDatabase();
|
||||
if (navyUnitDatabase) {
|
||||
this.#uploadDatabase(navyUnitDatabase, "navyUnitDatabase", "Navy Unit database", () => {
|
||||
this.#app?.getAircraftDatabase().load(() => {});
|
||||
this.#app?.getHelicopterDatabase().load(() => {});
|
||||
this.#app?.getGroundUnitDatabase().load(() => {});
|
||||
this.#app?.getNavyUnitDatabase().load(() => {});
|
||||
|
||||
this.#app?.getServerManager().reloadDatabases(() => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("Olympus core databases reloaded");
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets the databases to the default values
|
||||
*
|
||||
*/
|
||||
#resetToDefaultDatabases() {
|
||||
this.#resetToDefaultDatabase("aircraftdatabase", "Aircraft database", () => {
|
||||
this.#app?.getAircraftDatabase().load(() => {
|
||||
this.#resetToDefaultDatabase("helicopterdatabase", "Helicopter database", () => {
|
||||
this.#app?.getHelicopterDatabase().load(() => {
|
||||
this.#resetToDefaultDatabase("groundunitdatabase", "Ground Unit database", () => {
|
||||
this.#app?.getGroundUnitDatabase().load(() => {
|
||||
this.#resetToDefaultDatabase("navyunitdatabase", "Navy Unit database", () => {
|
||||
this.#app?.getNavyUnitDatabase().load(() => {
|
||||
this.#loadDatabases();
|
||||
|
||||
this.#app?.getServerManager().reloadDatabases(() => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("Olympus core databases reloaded");
|
||||
})
|
||||
|
||||
this.#hideAll();
|
||||
this.#aircraftEditor.show();
|
||||
this.#button1.classList.add("selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Restores the databases to the previous saved values. This is useful if you saved the databases by mistake and want to undo the error.
|
||||
*
|
||||
*/
|
||||
#restoreToPreviousDatabases() {
|
||||
this.#restoreToPreviousDatabase("aircraftdatabase", "Aircraft database", () => {
|
||||
this.#app?.getAircraftDatabase().load(() => {
|
||||
this.#restoreToPreviousDatabase("helicopterdatabase", "Helicopter database", () => {
|
||||
this.#app?.getHelicopterDatabase().load(() => {
|
||||
this.#restoreToPreviousDatabase("groundunitdatabase", "Ground Unit database", () => {
|
||||
this.#app?.getGroundUnitDatabase().load(() => {
|
||||
this.#restoreToPreviousDatabase("navyunitdatabase", "Navy Unit database", () => {
|
||||
this.#app?.getNavyUnitDatabase().load(() => {
|
||||
this.#loadDatabases();
|
||||
|
||||
this.#app?.getServerManager().reloadDatabases(() => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("Olympus core databases reloaded");
|
||||
})
|
||||
|
||||
this.#hideAll();
|
||||
this.#aircraftEditor.show();
|
||||
this.#button1.classList.add("selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Upload a single database to the server
|
||||
*
|
||||
* @param database The database
|
||||
* @param name The name of the database as it will be saved on the server
|
||||
* @param label A label used in the info popup
|
||||
*/
|
||||
#uploadDatabase(database: { blueprints: { [key: string]: UnitBlueprint } }, name: string, label: string, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/api/databases/save/units/" + name);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200) {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText(label + " saved successfully");
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while saving the " + label);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res: any) => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while saving the " + label);
|
||||
}
|
||||
xmlHttp.send(JSON.stringify(database));
|
||||
}
|
||||
|
||||
/** Resets a database to its default values on the server. NOTE: this only resets the database on the server, it will not reload it in the app.
|
||||
*
|
||||
* @param name The name of the database as it is saved on the server
|
||||
* @param label A label used in the info popup
|
||||
* @param callback Called when the operation is completed
|
||||
*/
|
||||
#resetToDefaultDatabase(name: string, label: string, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/api/databases/reset/units/" + name);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200) {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText(label + " reset successfully");
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while resetting the " + label);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res: any) => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while resetting the " + label)
|
||||
}
|
||||
xmlHttp.send("");
|
||||
}
|
||||
|
||||
/** Restores a database to its previously saved values on the server. NOTE: this only restores the database on the server, it will not reload it in the app.
|
||||
*
|
||||
* @param name The name of the database as it is saved on the server
|
||||
* @param label A label used in the info popup
|
||||
* @param callback Called when the operation is completed
|
||||
*/
|
||||
#restoreToPreviousDatabase(name: string, label: string, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/api/databases/restore/units/" + name);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200) {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText(label + " restored successfully");
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while restoring the " + label);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res: any) => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while restoring the " + label)
|
||||
}
|
||||
xmlHttp.send("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { UnitBlueprint } from "interfaces";
|
||||
import { UnitEditor } from "./uniteditor";
|
||||
import { addCheckboxInput, addDropdownInput, addStringInput } from "./utils";
|
||||
|
||||
/** Database editor for ground units
|
||||
*
|
||||
*/
|
||||
export class GroundUnitEditor extends UnitEditor {
|
||||
#blueprint: UnitBlueprint | null = null;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
super(contentDiv1, contentDiv2, contentDiv3);
|
||||
}
|
||||
|
||||
/** Sets a unit blueprint as the currently active one
|
||||
*
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint: UnitBlueprint) {
|
||||
this.#blueprint = blueprint;
|
||||
|
||||
if (this.#blueprint !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Unit properties";
|
||||
this.contentDiv2.appendChild(title);
|
||||
|
||||
addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => {blueprint.name = value; }, true);
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; });
|
||||
addStringInput(this.contentDiv2, "Type", blueprint.type?? "", "text", (value: string) => {blueprint.type = value; });
|
||||
addStringInput(this.contentDiv2, "Unit when grouped", blueprint.unitWhenGrouped?? "", "text", (value: string) => {blueprint.unitWhenGrouped = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost)?? "", "number", (value: string) => {blueprint.cost = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Acquisition range [m]", String(blueprint.acquisitionRange)?? "", "number", (value: string) => {blueprint.acquisitionRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Engagement range [m]", String(blueprint.engagementRange)?? "", "number", (value: string) => {blueprint.engagementRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Targeting range [m]", String(blueprint.targetingRange)?? "", "number", (value: string) => {blueprint.targetingRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Aim method range [m]", String(blueprint.aimMethodRange)?? "", "number", (value: string) => {blueprint.aimMethodRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Barrel height [m]", String(blueprint.barrelHeight)?? "", "number", (value: string) => {blueprint.barrelHeight = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Muzzle velocity [m/s]", String(blueprint.muzzleVelocity)?? "", "number", (value: string) => {blueprint.muzzleVelocity = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Aim time [s]", String(blueprint.aimTime)?? "", "number", (value: string) => {blueprint.aimTime = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Shots to fire", String(blueprint.shotsToFire)?? "", "number", (value: string) => {blueprint.shotsToFire = Math.round(parseFloat(value)); });
|
||||
addStringInput(this.contentDiv2, "Shots base interval [s]", String(blueprint.shotsBaseInterval)?? "", "number", (value: string) => {blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
|
||||
addStringInput(this.contentDiv2, "Shots base scatter [°]", String(blueprint.shotsBaseScatter)?? "", "number", (value: string) => {blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
|
||||
addStringInput(this.contentDiv2, "Alertness time constant [s]", String(blueprint.alertnessTimeConstant)?? "", "number", (value: string) => {blueprint.alertnessTimeConstant = Math.round(parseFloat(value)); });
|
||||
addCheckboxInput(this.contentDiv2, "Can target point", blueprint.canTargetPoint ?? false, (value: boolean) => {blueprint.canTargetPoint = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Can rearm", blueprint.canRearm ?? false, (value: boolean) => {blueprint.canRearm = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Can operate as AAA", blueprint.canAAA ?? false, (value: boolean) => {blueprint.canAAA = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Indirect fire (e.g. mortar)", blueprint.indirectFire ?? false, (value: boolean) => {blueprint.indirectFire = value;})
|
||||
addStringInput(this.contentDiv2, "Description", blueprint.description ?? "", "text", (value: string) => {blueprint.description = value; });
|
||||
addStringInput(this.contentDiv2, "Tags", blueprint.tags ?? "", "text", (value: string) => {blueprint.tags = value; });
|
||||
addStringInput(this.contentDiv2, "Marker file", blueprint.markerFile ?? "", "text", (value: string) => {blueprint.markerFile = value; });
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty blueprint
|
||||
*
|
||||
* @param key Blueprint key
|
||||
*/
|
||||
addBlueprint(key: string) {
|
||||
if (this.database != null) {
|
||||
this.database.blueprints[key] = {
|
||||
name: key,
|
||||
coalition: "",
|
||||
label: "",
|
||||
shortLabel: "",
|
||||
era: "",
|
||||
enabled: true
|
||||
}
|
||||
this.show();
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
frontend/website/plugins/databasemanager/src/index.ts
Normal file
5
frontend/website/plugins/databasemanager/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { DatabaseManagerPlugin } from "./databasemanagerplugin";
|
||||
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new DatabaseManagerPlugin();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { LoadoutBlueprint } from "interfaces";
|
||||
import { addLoadoutItemsEditor, addStringInput, arrayToString, stringToArray } from "./utils";
|
||||
|
||||
/** The LoadoutEditor allows the user to edit a loadout
|
||||
*
|
||||
*/
|
||||
export class LoadoutEditor {
|
||||
#contentDiv: HTMLElement;
|
||||
#loadout: LoadoutBlueprint | null = null;
|
||||
#visible: boolean = false;
|
||||
|
||||
constructor(contentDiv: HTMLElement) {
|
||||
this.#contentDiv = contentDiv;
|
||||
this.#contentDiv.addEventListener("refresh", () => {
|
||||
if (this.#visible)
|
||||
this.show();
|
||||
})
|
||||
}
|
||||
|
||||
/** Set the loadout to edit
|
||||
*
|
||||
* @param loadout The loadout to edit
|
||||
*/
|
||||
setLoadout(loadout: LoadoutBlueprint) {
|
||||
this.#loadout = loadout;
|
||||
}
|
||||
|
||||
/** Show the editor
|
||||
*
|
||||
*/
|
||||
show() {
|
||||
this.#visible = true;
|
||||
this.#contentDiv.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Loadout properties";
|
||||
this.#contentDiv.appendChild(title);
|
||||
|
||||
if (this.#loadout) {
|
||||
var loadout = this.#loadout;
|
||||
addStringInput(this.#contentDiv, "Name", loadout.name, "text", (value: string) => {loadout.name = value; this.#contentDiv.dispatchEvent(new Event("refresh"));});
|
||||
addStringInput(this.#contentDiv, "Code", loadout.code, "text", (value: string) => {loadout.code = value; });
|
||||
addStringInput(this.#contentDiv, "Roles", arrayToString(loadout.roles), "text", (value: string) => {loadout.roles = stringToArray(value);});
|
||||
addLoadoutItemsEditor(this.#contentDiv, this.#loadout);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
this.#visible = false;
|
||||
this.#contentDiv.replaceChildren();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { UnitBlueprint } from "interfaces";
|
||||
import { UnitEditor } from "./uniteditor";
|
||||
import { addDropdownInput, addStringInput } from "./utils";
|
||||
|
||||
/** Database editor for navy units
|
||||
*
|
||||
*/
|
||||
export class NavyUnitEditor extends UnitEditor {
|
||||
#blueprint: UnitBlueprint | null = null;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
super(contentDiv1, contentDiv2, contentDiv3);
|
||||
}
|
||||
|
||||
/** Sets a unit blueprint as the currently active one
|
||||
*
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint: UnitBlueprint) {
|
||||
this.#blueprint = blueprint;
|
||||
|
||||
if (this.#blueprint !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Unit properties";
|
||||
this.contentDiv2.appendChild(title);
|
||||
|
||||
addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => {blueprint.name = value; }, true);
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; });
|
||||
addStringInput(this.contentDiv2, "Type", blueprint.type?? "", "text", (value: string) => {blueprint.type = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost)?? "", "number", (value: string) => {blueprint.cost = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Barrel height [m]", String(blueprint.barrelHeight)?? "", "number", (value: string) => {blueprint.barrelHeight = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Muzzle velocity [m/s]", String(blueprint.muzzleVelocity)?? "", "number", (value: string) => {blueprint.muzzleVelocity = parseFloat(value); });
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty blueprint
|
||||
*
|
||||
* @param key Blueprint key
|
||||
*/
|
||||
addBlueprint(key: string) {
|
||||
if (this.database != null) {
|
||||
this.database.blueprints[key] = {
|
||||
name: key,
|
||||
coalition: "",
|
||||
label: "",
|
||||
shortLabel: "",
|
||||
era: "",
|
||||
enabled: true
|
||||
}
|
||||
this.show();
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
117
frontend/website/plugins/databasemanager/src/uniteditor.ts
Normal file
117
frontend/website/plugins/databasemanager/src/uniteditor.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { UnitBlueprint } from "interfaces";
|
||||
import { UnitDatabase } from "unit/databases/unitdatabase";
|
||||
import { addBlueprintsScroll, addNewElementInput } from "./utils";
|
||||
|
||||
/** Base abstract class of Unit database editors
|
||||
*
|
||||
*/
|
||||
export abstract class UnitEditor {
|
||||
blueprint: UnitBlueprint | null = null;
|
||||
database: {blueprints: {[key: string]: UnitBlueprint}} | null = null;
|
||||
visible: boolean = false;
|
||||
contentDiv1: HTMLElement;
|
||||
contentDiv2: HTMLElement;
|
||||
contentDiv3: HTMLElement;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
this.contentDiv1 = contentDiv1;
|
||||
this.contentDiv2 = contentDiv2;
|
||||
this.contentDiv3 = contentDiv3;
|
||||
|
||||
/* Refresh the list of units if it changes */
|
||||
this.contentDiv1.addEventListener("refresh", () => {
|
||||
if (this.visible)
|
||||
this.show();
|
||||
})
|
||||
|
||||
/* If the unit properties or loadout are edited, reload the editor */
|
||||
this.contentDiv2.addEventListener("refresh", () => {
|
||||
if (this.visible) {
|
||||
if (this.blueprint !== null)
|
||||
this.setBlueprint(this.blueprint);
|
||||
}
|
||||
});
|
||||
|
||||
this.contentDiv3.addEventListener("refresh", () => {
|
||||
if (this.visible) {
|
||||
if (this.blueprint !== null)
|
||||
this.setBlueprint(this.blueprint);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param database The database that the editor will operate on
|
||||
*/
|
||||
setDatabase(database: UnitDatabase) {
|
||||
this.database = JSON.parse(JSON.stringify({blueprints: database.getBlueprints(true)}));
|
||||
}
|
||||
|
||||
/** Show the editor
|
||||
* @param filter String filter
|
||||
*/
|
||||
show(filter: string = "") {
|
||||
this.visible = true;
|
||||
this.contentDiv1.replaceChildren();
|
||||
this.contentDiv2.replaceChildren();
|
||||
this.contentDiv3.replaceChildren();
|
||||
|
||||
/* Create the list of units. Each unit is clickable to activate the editor on it */
|
||||
if (this.database != null) {
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Units list";
|
||||
this.contentDiv1.appendChild(title);
|
||||
|
||||
var filterInput = document.createElement("input");
|
||||
filterInput.value = filter;
|
||||
this.contentDiv1.appendChild(filterInput);
|
||||
|
||||
filterInput.onchange = (e: Event) => {
|
||||
this.show((e.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
this.addBlueprints(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
this.visible = false;
|
||||
this.contentDiv1.replaceChildren();
|
||||
this.contentDiv2.replaceChildren();
|
||||
this.contentDiv3.replaceChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The edited database
|
||||
*/
|
||||
getDatabase() {
|
||||
return this.database;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filter String filter
|
||||
*/
|
||||
addBlueprints(filter: string = "") {
|
||||
if (this.database) {
|
||||
addBlueprintsScroll(this.contentDiv1, this.database, filter, (key: string) => {
|
||||
if (this.database != null)
|
||||
this.setBlueprint(this.database.blueprints[key])
|
||||
});
|
||||
|
||||
addNewElementInput(this.contentDiv1, (ev: MouseEvent, input: HTMLInputElement) => {
|
||||
if (input.value != "")
|
||||
this.addBlueprint((input).value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* Abstract methods which will depend on the specific type of units */
|
||||
abstract setBlueprint(blueprint: UnitBlueprint): void;
|
||||
abstract addBlueprint(key: string): void;
|
||||
}
|
||||
297
frontend/website/plugins/databasemanager/src/utils.ts
Normal file
297
frontend/website/plugins/databasemanager/src/utils.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import { LoadoutBlueprint, LoadoutItemBlueprint, UnitBlueprint } from "interfaces";
|
||||
|
||||
/** This file contains a set of utility functions that are reused in the various editors and allows to declutter the classes
|
||||
*
|
||||
*/
|
||||
|
||||
/** Add a string input in the form of String: [ value ]
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input
|
||||
* @param key The key of the input, which will be used as label
|
||||
* @param value The initial value of the input
|
||||
* @param type The type of the input, e.g. "Text" or "Number" as per html standard
|
||||
* @param callback Callback called when the user enters a new value
|
||||
* @param disabled If true, the input will be disabled and read only
|
||||
*/
|
||||
export function addStringInput(div: HTMLElement, key: string, value: string, type: string, callback: CallableFunction, disabled?: boolean) {
|
||||
var row = document.createElement("div");
|
||||
var dt = document.createElement("dt");
|
||||
var dd = document.createElement("dd");
|
||||
dt.innerText = key;
|
||||
var input = document.createElement("input");
|
||||
input.value = value;
|
||||
input.textContent = value;
|
||||
input.type = type?? "text";
|
||||
input.disabled = disabled?? false;
|
||||
input.onchange = () => callback(input.value);
|
||||
dd.appendChild(input);
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
row.classList.add("input-row");
|
||||
div.appendChild(row);
|
||||
}
|
||||
|
||||
/** Add a dropdown (select) input
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input
|
||||
* @param key The key of the input, which will be used as label
|
||||
* @param value The initial value of the input
|
||||
* @param options The dropdown options
|
||||
*/
|
||||
export function addDropdownInput(div: HTMLElement, key: string, value: string, options: string[], callback: CallableFunction, disabled?: boolean) {
|
||||
var row = document.createElement("div");
|
||||
var dt = document.createElement("dt");
|
||||
var dd = document.createElement("dd");
|
||||
dt.innerText = key;
|
||||
var select = document.createElement("select");
|
||||
options.forEach((option: string) => {
|
||||
var el = document.createElement("option");
|
||||
el.value = option;
|
||||
el.innerText = option;
|
||||
select.appendChild(el);
|
||||
});
|
||||
select.value = value;
|
||||
select.disabled = disabled?? false;
|
||||
select.onchange = () => callback(select.value);
|
||||
dd.appendChild(select);
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
row.classList.add("input-row");
|
||||
div.appendChild(row);
|
||||
}
|
||||
|
||||
/** Add a checkbox input in the form of String: [ value ]
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input
|
||||
* @param key The key of the input, which will be used as label
|
||||
* @param value The initial value of the input
|
||||
* @param callback Callback called when the user enters a new value
|
||||
* @param disabled If true, the input will be disabled and read only
|
||||
*/
|
||||
export function addCheckboxInput(div: HTMLElement, key: string, value: boolean, callback: CallableFunction, disabled?: boolean) {
|
||||
var row = document.createElement("div");
|
||||
var dt = document.createElement("dt");
|
||||
var dd = document.createElement("dd");
|
||||
dt.innerText = key;
|
||||
var input = document.createElement("input");
|
||||
input.checked = value;
|
||||
input.type = "checkbox";
|
||||
input.disabled = disabled?? false;
|
||||
input.onchange = () => callback(input.checked);
|
||||
dd.appendChild(input);
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
row.classList.add("input-row");
|
||||
div.appendChild(row);
|
||||
}
|
||||
|
||||
/** Create a loadout items editor. This editor allows to add or remove loadout items, as well as changing their name and quantity
|
||||
*
|
||||
* @param div The HTMLElement that will contain the editor
|
||||
* @param loadout The loadout to edit
|
||||
*/
|
||||
export function addLoadoutItemsEditor(div: HTMLElement, loadout: LoadoutBlueprint) {
|
||||
var itemsEl = document.createElement("div");
|
||||
itemsEl.classList.add("dm-scroll-container", "dm-items-container");
|
||||
|
||||
/* Create a row for each loadout item to allow and change the name and quantity of the item itself */
|
||||
loadout.items.sort((a: LoadoutItemBlueprint, b: LoadoutItemBlueprint) => a.name.localeCompare(b.name, undefined, {sensitivity: 'base'}));
|
||||
loadout.items.forEach((item: LoadoutItemBlueprint, index: number) => {
|
||||
var rowDiv = document.createElement("div");
|
||||
|
||||
var nameLabel = document.createElement("label");
|
||||
nameLabel.innerText = "Name"
|
||||
rowDiv.appendChild(nameLabel);
|
||||
|
||||
var nameInput = document.createElement("input");
|
||||
rowDiv.appendChild(nameInput);
|
||||
nameInput.textContent = item.name;
|
||||
nameInput.value = item.name
|
||||
nameInput.onchange = () => { loadout.items[index].name = nameInput.value; }
|
||||
|
||||
var quantityLabel = document.createElement("label");
|
||||
quantityLabel.innerText = "Quantity"
|
||||
rowDiv.appendChild(quantityLabel);
|
||||
|
||||
var quantityInput = document.createElement("input");
|
||||
rowDiv.appendChild(quantityInput);
|
||||
quantityInput.textContent = String(item.quantity);
|
||||
quantityInput.value = String(item.quantity);
|
||||
quantityInput.type = "number";
|
||||
quantityInput.step = "1";
|
||||
quantityInput.onchange = () => { loadout.items[index].quantity = parseInt(quantityInput.value); }
|
||||
|
||||
/* This button allows to remove the item */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
loadout.items.splice(index, 1);
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
}
|
||||
rowDiv.appendChild(button);
|
||||
|
||||
itemsEl.appendChild(rowDiv);
|
||||
})
|
||||
div.appendChild(itemsEl);
|
||||
|
||||
/* Button to add a new item to the loadout */
|
||||
var inputDiv = document.createElement("div");
|
||||
inputDiv.classList.add("dm-new-item-input");
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "Add";
|
||||
inputDiv.appendChild(button);
|
||||
div.appendChild(inputDiv);
|
||||
|
||||
button.addEventListener("click", (ev: MouseEvent) => {
|
||||
loadout?.items.push({
|
||||
name: "",
|
||||
quantity: 1
|
||||
})
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
});
|
||||
}
|
||||
|
||||
/** Add a input and button to create a new element in a list. It uses a generic callback to actually add the element.
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input and button
|
||||
* @param callback Callback called when the user clicks on "Add"
|
||||
*/
|
||||
export function addNewElementInput(div: HTMLElement, callback: CallableFunction) {
|
||||
var inputDiv = document.createElement("div");
|
||||
inputDiv.classList.add("dm-new-element-input");
|
||||
|
||||
var input = document.createElement("input");
|
||||
inputDiv.appendChild(input);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "Add";
|
||||
button.addEventListener("click", (ev: MouseEvent) => callback(ev, input));
|
||||
inputDiv.appendChild(button);
|
||||
div.appendChild(inputDiv);
|
||||
}
|
||||
|
||||
/** Add a scrollable list of blueprints
|
||||
*
|
||||
* @param div The HTMLElement that will contain the list
|
||||
* @param database The database that will be used to fill the list of blueprints
|
||||
* @param filter A string filter that will be executed to filter the blueprints to add
|
||||
* @param callback Callback called when the user clicks on one of the elements
|
||||
*/
|
||||
export function addBlueprintsScroll(div: HTMLElement, database: {blueprints: {[key: string]: UnitBlueprint}}, filter: string, callback: CallableFunction) {
|
||||
var scrollDiv = document.createElement("div");
|
||||
scrollDiv.classList.add("dm-scroll-container");
|
||||
if (database !== null) {
|
||||
var blueprints: {[key: string]: UnitBlueprint} = database.blueprints;
|
||||
|
||||
for (let key of Object.keys(blueprints).sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))) {
|
||||
var addKey = true;
|
||||
if (filter !== "") {
|
||||
try {
|
||||
var blueprint = blueprints[key];
|
||||
addKey = eval(filter);
|
||||
} catch {
|
||||
console.error("An error has occurred evaluating the blueprint filter")
|
||||
}
|
||||
}
|
||||
|
||||
if (addKey) {
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
|
||||
let text = document.createElement("div");
|
||||
text.innerHTML = `<div>${key}</div> <div>${blueprints[key].label}</div>`;
|
||||
text.onclick = () => {
|
||||
callback(key);
|
||||
const collection = document.getElementsByClassName("blueprint-selected");
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
collection[i].classList.remove("blueprint-selected");
|
||||
}
|
||||
text.classList.add("blueprint-selected");
|
||||
}
|
||||
rowDiv.appendChild(text);
|
||||
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = blueprints[key].enabled;
|
||||
checkbox.onclick = () => {
|
||||
console.log(checkbox.checked);
|
||||
blueprints[key].enabled = checkbox.checked;
|
||||
}
|
||||
rowDiv.appendChild(checkbox);
|
||||
|
||||
/* This button allows to remove an element from the list. It requires a refresh. */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
delete blueprints[key];
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
}
|
||||
rowDiv.appendChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
div.appendChild(scrollDiv);
|
||||
}
|
||||
|
||||
/** Add a scrollable list of loadouts
|
||||
*
|
||||
* @param div The HTMLElement that will contain the list
|
||||
* @param loadouts The loadouts that will be used to fill the list
|
||||
* @param callback Callback called when the user clicks on one of the elements
|
||||
*/
|
||||
export function addLoadoutsScroll(div: HTMLElement, loadouts: LoadoutBlueprint[], callback: CallableFunction) {
|
||||
var loadoutsEl = document.createElement("div");
|
||||
loadoutsEl.classList.add("dm-scroll-container", "dm-loadout-container")
|
||||
|
||||
loadouts.sort((a: LoadoutBlueprint, b: LoadoutBlueprint) => a.name.localeCompare(b.name, undefined, {sensitivity: 'base'}));
|
||||
loadouts.forEach((loadout: LoadoutBlueprint, index: number) => {
|
||||
var rowDiv = document.createElement("div");
|
||||
loadoutsEl.appendChild(rowDiv);
|
||||
|
||||
var text = document.createElement("label");
|
||||
text.textContent = loadout.name;
|
||||
text.onclick = () => { callback(loadout) };
|
||||
rowDiv.appendChild(text);
|
||||
|
||||
/* The "Empty loadout" can not be removed */
|
||||
if (loadout.name !== "Empty loadout") {
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = loadout.enabled;
|
||||
checkbox.onclick = () => {
|
||||
console.log(checkbox.checked);
|
||||
loadout.enabled = checkbox.checked;
|
||||
}
|
||||
rowDiv.appendChild(checkbox);
|
||||
|
||||
/* This button allows to remove an element from the list. It requires a refresh. */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
loadouts.splice(index, 1);
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
}
|
||||
rowDiv.appendChild(button);
|
||||
}
|
||||
});
|
||||
|
||||
div.appendChild(loadoutsEl);
|
||||
}
|
||||
|
||||
/** Converts an array of string into a single string like [val1, val2, val3]
|
||||
*
|
||||
* @param array The input array of strings
|
||||
* @returns The string
|
||||
*/
|
||||
export function arrayToString(array: string[]) {
|
||||
return "[" + array.join( ", " ) + "]";
|
||||
}
|
||||
|
||||
/** Converts an a single string like [val1, val2, val3] into an array
|
||||
*
|
||||
* @param input The input string
|
||||
* @returns The array
|
||||
*/
|
||||
export function stringToArray(input: string) {
|
||||
return input.match( /(\w)+/g ) ?? [];
|
||||
}
|
||||
Reference in New Issue
Block a user