diff --git a/client/public/stylesheets/style/style.css b/client/public/stylesheets/style/style.css
index f72e558b..2117bd1d 100644
--- a/client/public/stylesheets/style/style.css
+++ b/client/public/stylesheets/style/style.css
@@ -759,20 +759,6 @@ nav.ol-panel> :last-child {
}
/****************************************************************************************/
-#unit-import-export-dialog th {
- padding:4px 8px;
-}
-
-#unit-import-export-dialog tr :first-child {
- text-align: left;
-}
-
-#unit-import-export-dialog td {
- color:white;
- text-align: center;
-}
-
-
#splash-screen {
border-radius: var(--border-radius-md);
overflow: hidden;
@@ -1306,6 +1292,10 @@ dl.ol-data-grid dd {
width: 16px;
}
+.ol-checkbox input[type="checkbox"]:disabled:before {
+ opacity: 10%;
+}
+
.ol-checkbox input[type="checkbox"]:checked::before {
background-image: url("/resources/theme/images/icons/square-check-solid.svg");
}
@@ -1516,3 +1506,50 @@ input[type=number]::-webkit-outer-spin-button {
.ol-log-entry {
border-bottom: 1px solid #FFFFFF44;
}
+
+.file-import-export {
+ max-width: 500px;
+}
+
+.file-import-export .ol-dialog-content {
+ display:flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.file-import-export p {
+ background-color: var(--background-grey);
+ border-left:6px solid var(--secondary-blue-text);
+ padding:12px;
+}
+
+.file-import-export th {
+ padding:4px 6px;
+}
+
+.file-import-export tr td:first-child {
+ text-align: left;
+}
+
+.file-import-export td {
+ color:white;
+ text-align: center;
+}
+
+.file-import-export .ol-checkbox {
+ display:flex;
+ justify-content: center;
+}
+
+.file-import-export .ol-checkbox input::before {
+ margin-right: 0;
+}
+
+.file-import-export .ol-checkbox span {
+ display:none;
+}
+
+.file-import-export button.start-transfer {
+ background-color: var(--secondary-blue-text);
+ border-color: var(--secondary-blue-text);
+}
\ No newline at end of file
diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts
index 64f67089..aca47b3b 100644
--- a/client/src/other/utils.ts
+++ b/client/src/other/utils.ts
@@ -434,14 +434,24 @@ export function convertDateAndTimeToDate(dateAndTime: DateAndTime) {
return new Date(year, month, date.Day, time.h, time.m, time.s);
}
-export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}) {
+export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}, options?:any) {
+ options = {
+ "disabled": false,
+ "name": "",
+ "readOnly": false,
+ ...options
+ };
var div = document.createElement("div");
div.classList.add("ol-checkbox");
var label = document.createElement("label");
label.title = text;
var input = document.createElement("input");
input.type = "checkbox";
- input.checked = checked;
+ input.checked = checked;
+ input.name = options.name;
+ input.disabled = options.disabled;
+ input.readOnly = options.readOnly;
+ input.value = value;
var span = document.createElement("span");
span.innerText = value;
label.appendChild(input);
diff --git a/client/src/unit/unitdatafile.ts b/client/src/unit/unitdatafile.ts
index 91baddd1..1994bc6d 100644
--- a/client/src/unit/unitdatafile.ts
+++ b/client/src/unit/unitdatafile.ts
@@ -1,3 +1,58 @@
+import { Dialog } from "../dialog/dialog";
+import { createCheckboxOption } from "../other/utils";
+
export abstract class UnitDataFile {
+
+ protected data:any;
+ protected dialog!:Dialog;
+
constructor() {}
+
+ buildCategoryCoalitionTable() {
+
+ const categories = this.#getCategoriesFromData();
+ const coalitions = ["blue", "neutral", "red"];
+
+ let headersHTML:string = ``;
+ let matrixHTML:string = ``;
+
+ categories.forEach((category:string, index) => {
+ matrixHTML += `
| ${category} | `;
+
+ coalitions.forEach((coalition:string) => {
+ if (index === 0)
+ headersHTML += `${coalition[0].toUpperCase()+coalition.substring(1)} | `;
+
+ const optionIsValid = this.data[category].hasOwnProperty(coalition);
+ let checkboxHTML = createCheckboxOption(`${category}:${coalition}`, category, optionIsValid, () => {}, {
+ "disabled": !optionIsValid,
+ "name": "category-coalition-selection",
+ "readOnly": !optionIsValid
+ }).outerHTML;
+
+ if (optionIsValid)
+ checkboxHTML = checkboxHTML.replace( `"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up
+
+ matrixHTML += `${checkboxHTML} | `;
+
+ });
+ matrixHTML += "
";
+ });
+
+ const table = this.dialog.getElement().querySelector("table.categories-coalitions");
+
+ (table.tHead).innerHTML = `| | ${headersHTML}
`;
+ (table.querySelector(`tbody`)).innerHTML = matrixHTML;
+
+ }
+
+ #getCategoriesFromData() {
+ const categories = Object.keys(this.data);
+ categories.sort();
+ return categories;
+ }
+
+ getData() {
+ return this.data;
+ }
}
\ No newline at end of file
diff --git a/client/src/unit/unitdatafileexport.ts b/client/src/unit/unitdatafileexport.ts
index 013d49a5..2085381f 100644
--- a/client/src/unit/unitdatafileexport.ts
+++ b/client/src/unit/unitdatafileexport.ts
@@ -1,5 +1,4 @@
import { getApp } from "..";
-import { GROUND_UNIT_AIR_DEFENCE_REGEX } from "../constants/constants";
import { Dialog } from "../dialog/dialog";
import { zeroAppend } from "../other/utils";
import { Unit } from "./unit";
@@ -7,20 +6,16 @@ import { UnitDataFile } from "./unitdatafile";
export class UnitDataFileExport extends UnitDataFile {
- #data!:any;
- #dialog:Dialog;
+ protected data!:any;
+ protected dialog:Dialog;
#element!:HTMLElement;
- #categoryCoalitionHeaders!: HTMLElement;
- #categoryCoalitionMatrix!: HTMLElement;
constructor( elementId:string ) {
super();
- this.#dialog = new Dialog(elementId);
- this.#element = this.#dialog.getElement();
- this.#categoryCoalitionMatrix = this.#element.querySelector("tbody");
- this.#categoryCoalitionHeaders = this.#element.querySelector("thead");
+ this.dialog = new Dialog(elementId);
+ this.#element = this.dialog.getElement();
- this.#element.querySelector(".start-transfer.export")?.addEventListener("click", (ev:MouseEventInit) => {
+ this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev:MouseEventInit) => {
this.#doExport();
});
}
@@ -28,56 +23,27 @@ export class UnitDataFileExport extends UnitDataFile {
/**
* Show the form to start the export journey
*/
- showForm(units:Unit[]) {
- this.#element.setAttribute( "data-mode", "export" );
-
+ showForm(units:Unit[]) {
const data:any = {};
- const categories:string[] = [];
- const coalitions:string[] = [];
const unitCanBeExported = (unit:Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory());
units.filter((unit:Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit:Unit) => {
- const category = unit.getCategoryLabel();
+ const category = unit.getCategory();
const coalition = unit.getCoalition();
- if (!coalitions.includes(coalition))
- coalitions.push(coalition);
-
if (!data.hasOwnProperty(category)) {
data[category] = {};
- categories.push(category);
}
- // Cache unit data
if (!data[category].hasOwnProperty(coalition))
data[category][coalition] = [];
data[category][coalition].push(unit);
});
- this.#data = data;
-
- categories.sort();
- coalitions.sort();
-
- let headersHTML:string = ``;
- let matrixHTML:string = ``;
-
- categories.forEach((category:string, index) => {
- matrixHTML += `| ${category} | `;
-
- coalitions.forEach((coalition:string) => {
- if (index === 0)
- headersHTML += `${coalition} | `;
- matrixHTML += ` | `;
- });
-
- matrixHTML += "
";
- });
-
- this.#categoryCoalitionHeaders.innerHTML = `| | ${headersHTML}
`;
- this.#categoryCoalitionMatrix.innerHTML = matrixHTML;
- this.#dialog.show();
+ this.data = data;
+ this.buildCategoryCoalitionTable();
+ this.dialog.show();
}
#doExport() {
@@ -87,7 +53,7 @@ export class UnitDataFileExport extends UnitDataFile {
this.#element.querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox:HTMLInputElement) => {
if (checkbox instanceof HTMLInputElement) {
const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
- selectedUnits = selectedUnits.concat(this.#data[category][coalition]);
+ selectedUnits = selectedUnits.concat(this.data[category][coalition]);
}
});
@@ -111,7 +77,7 @@ export class UnitDataFileExport extends UnitDataFile {
a.href = URL.createObjectURL(file);
a.download = `olympus_${getApp().getMissionManager().getTheatre().replace( /[^\w]/gi, "" ).toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth()+1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`;
a.click();
- this.#dialog.hide();
+ this.dialog.hide();
}
}
\ No newline at end of file
diff --git a/client/src/unit/unitdatafileimport.ts b/client/src/unit/unitdatafileimport.ts
index e91af4d0..b883c150 100644
--- a/client/src/unit/unitdatafileimport.ts
+++ b/client/src/unit/unitdatafileimport.ts
@@ -1,43 +1,110 @@
import { getApp } from "..";
-import { GROUND_UNIT_AIR_DEFENCE_REGEX } from "../constants/constants";
import { Dialog } from "../dialog/dialog";
-import { zeroAppend } from "../other/utils";
-import { Unit } from "./unit";
+import { UnitData } from "../interfaces";
import { UnitDataFile } from "./unitdatafile";
export class UnitDataFileImport extends UnitDataFile {
- #data!:any;
- #dialog:Dialog;
- #element!:HTMLElement;
- #categoryCoalitionHeaders!: HTMLElement;
- #categoryCoalitionMatrix!: HTMLElement;
+ protected data!:any;
+ protected dialog:Dialog;
+ #fileData!:{[key:string]: UnitData[]};
constructor( elementId:string ) {
super();
- this.#dialog = new Dialog(elementId);
- this.#element = this.#dialog.getElement();
- this.#categoryCoalitionMatrix = this.#element.querySelector("tbody");
- this.#categoryCoalitionHeaders = this.#element.querySelector("thead");
-
- // this.#element.querySelector(".start-transfer.import")?.addEventListener("click", (ev:MouseEventInit) => {
- // this.#doImport();
- // });
+ this.dialog = new Dialog(elementId);
+ this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev:MouseEventInit) => {
+ this.#doImport();
+ this.dialog.hide();
+ });
}
#doImport() {
-
- /*
- for (let groupName in groups) {
- if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: UnitData) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) {
- var aliveUnits = groups[groupName].filter((unit: UnitData) => { return unit.alive });
- var units = aliveUnits.map((unit: UnitData) => {
- return { unitType: unit.name, location: unit.position, liveryID: "" }
- });
- getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
- }
- }
- //*/
+
+ let selectedCategories:any = {};
+ const unitsManager = getApp().getUnitsManager();
+
+ this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox:HTMLInputElement) => {
+ if (checkbox instanceof HTMLInputElement) {
+ const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
+ selectedCategories[category] = selectedCategories[category] || {};
+ selectedCategories[category][coalition] = true;
+ }
+ });
+
+ for (const[groupName, groupData] of Object.entries(this.#fileData)) {
+ if (groupName === "" || groupData.length === 0 || !this.#unitGroupDataCanBeImported(groupData))
+ continue;
+
+ let { category, coalition } = groupData[0];
+
+ if (!selectedCategories.hasOwnProperty(category)
+ || !selectedCategories[category].hasOwnProperty(coalition)
+ || selectedCategories[category][coalition] !== true )
+ continue;
+
+ let unitsToSpawn = groupData.filter((unitData:UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData:UnitData) => {
+ return { unitType: unitData.name, location: unitData.position, liveryID: "" }
+ });
+
+ unitsManager.spawnUnits(category, unitsToSpawn, coalition, true);
+ }
+
+ /*
+ for (let groupName in groups) {
+ if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: UnitData) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) {
+ var aliveUnits = groups[groupName].filter((unit: UnitData) => { return unit.alive });
+ var units = aliveUnits.map((unit: UnitData) => {
+ return { unitType: unit.name, location: unit.position, liveryID: "" }
+ });
+ getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
+ }
+ }
+ //*/
+ }
+
+ #showForm() {
+ const data:any = {};
+
+ for (const [ group, units ] of Object.entries(this.#fileData) ) {
+ if (group === "" || units.length === 0)
+ continue;
+
+ if (units.some((unit:UnitData) => !this.#unitDataCanBeImported(unit)))
+ continue;
+
+ const category = units[0].category;
+
+ if (!data.hasOwnProperty(category)) {
+ data[category] = {};
+ }
+
+ units.forEach((unit:UnitData) => {
+ if (!data[category].hasOwnProperty(unit.coalition))
+ data[category][unit.coalition] = [];
+
+ data[category][unit.coalition].push(unit);
+ });
+
+ }
+
+ /*
+ groups.filter((unit:Unit) => unitCanBeImported(unit)).forEach((unit:Unit) => {
+ const category = unit.getCategoryLabel();
+ const coalition = unit.getCoalition();
+
+ if (!data.hasOwnProperty(category)) {
+ data[category] = {};
+ }
+
+ if (!data[category].hasOwnProperty(coalition))
+ data[category][coalition] = [];
+
+ data[category][coalition].push(unit);
+ });
+ //*/
+ this.data = data;
+ this.buildCategoryCoalitionTable();
+ this.dialog.show();
}
selectFile() {
@@ -49,14 +116,23 @@ export class UnitDataFileImport extends UnitDataFile {
return;
}
var reader = new FileReader();
- reader.onload = function (e: any) {
- var contents = e.target.result;
- var groups = JSON.parse(contents);
- console.log(groups);
+ reader.onload = (e: any) => {
+ this.#fileData = JSON.parse(e.target.result);
+ this.#showForm();
};
reader.readAsText(file);
})
input.click();
}
+ #unitDataCanBeImported(unitData:UnitData) {
+ return unitData.alive && this.#unitGroupDataCanBeImported([unitData]);
+ }
+
+ #unitGroupDataCanBeImported(unitGroupData:UnitData[]) {
+ return unitGroupData.every((unitData:UnitData) => {
+ return !["Aircraft", "Helicopter"].includes(unitData.category);
+ }) && unitGroupData.some((unitData:UnitData) => unitData.alive);
+ }
+
}
\ No newline at end of file
diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts
index 5e18e696..825ffef7 100644
--- a/client/src/unit/unitsmanager.ts
+++ b/client/src/unit/unitsmanager.ts
@@ -1006,7 +1006,7 @@ export class UnitsManager {
*/
exportToFile() {
if (!this.#unitDataExport)
- this.#unitDataExport = new UnitDataFileExport("unit-import-export-dialog");
+ this.#unitDataExport = new UnitDataFileExport("unit-export-dialog");
this.#unitDataExport.showForm(Object.values(this.#units));
}
@@ -1015,7 +1015,7 @@ export class UnitsManager {
*/
importFromFile() {
if (!this.#unitDataImport)
- this.#unitDataImport = new UnitDataFileImport("unit-import-export-dialog");
+ this.#unitDataImport = new UnitDataFileImport("unit-import-dialog");
this.#unitDataImport.selectFile();
}
diff --git a/client/views/other/dialogs.ejs b/client/views/other/dialogs.ejs
index dc7c769d..e1e59b2f 100644
--- a/client/views/other/dialogs.ejs
+++ b/client/views/other/dialogs.ejs
@@ -1,6 +1,17 @@
<%- include('dialogs/advancedsettings.ejs') %>
<%- include('dialogs/commandmodesettings.ejs') %>
<%- include('dialogs/customformation.ejs') %>
-<%- include('dialogs/importexport.ejs') %>
+<%- include('dialogs/importexport.ejs', {
+ "dialogId": "unit-export-dialog",
+ "submitButtonText": "Export units to file",
+ "textContent": "Select the unit categories you would like to export. Note: only ground and naval units can be exported at this time.",
+ "title": "Export"
+}) %>
+<%- include('dialogs/importexport.ejs', {
+ "dialogId": "unit-import-dialog",
+ "submitButtonText": "Import units into mission",
+ "textContent": "Select the unit categories you would like to import.",
+ "title": "Import"
+}) %>
<%- include('dialogs/slowdelete.ejs') %>
<%- include('dialogs/splash.ejs') %>
\ No newline at end of file
diff --git a/client/views/other/dialogs/importexport.ejs b/client/views/other/dialogs/importexport.ejs
index b33a73a3..6f10f716 100644
--- a/client/views/other/dialogs/importexport.ejs
+++ b/client/views/other/dialogs/importexport.ejs
@@ -1,11 +1,11 @@
-
+
-
Note: only ground and naval units can be exported at this time. (Air units will be possible soon.)
-
+ <%= textContent %>
+
@@ -14,8 +14,7 @@
\ No newline at end of file