Continued typescript conversion

This commit is contained in:
Pax1601 2023-01-08 22:33:38 +01:00
parent ca6b66eb33
commit 0f689810d7
41 changed files with 2431 additions and 791 deletions

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ core.vcxproj.user
.vscode
*.user
Output
server
/www/js
/www/node_modules

View File

@ -1,6 +1,7 @@
:root {
--blue: #2196F3aa;
--red: #f32121aa;
--dark: #202831AA;
--normal: #2196F3aa;
--highlight: #FFFFFFAA;
--start_angle: 0deg;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
www/img/buttons/rtb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
www/img/buttons/tanker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

View File

@ -20,30 +20,9 @@
integrity="sha256-o9N1jGDZrf5tS+Ft4gbIK7mYMipq9lqpVJ91xHSyKhg="
crossorigin=""></script>
<script src="js/DCS/payloadNames.js"></script>
<script src="js/DCS/DCSCommands.js"></script>
<script src="js/Panels/PanelButton.js"></script>
<script src="js/Panels/SettingsPanel.js"></script>
<script src="js/Panels/UnitInfoPanel.js"></script>
<script src="js/Panels/UnitControlPanel.js"></script>
<script src="js/Panels/FormationControlPanel.js"></script>
<script src="js/Panels/ActionPanel.js"></script>
<script type = "module" src="js/Units/UnitMarker.js"></script>
<script type = "module" src="js/index.js"></script>
<script src="js/Units/unitTypes.js"></script>
<script src="js/Units/Unit.js"></script>
<script src="js/Units/UnitMarker.js"></script>
<script src="js/Units/UnitsManager.js"></script>
<script src="js/Other/Utils.js"></script>
<script src="js/Other/AirbaseMarker.js"></script>
<script src="js/Other/MissionData.js"></script>
<script src="js/Map/Map.js"></script>
<script src="js/Map/SelectionWheel.js"></script>
<script src="js/Map/SelectionScroll.js"></script>
<script src="js/index.js"></script>
</head>
<body>
<table id="content-table">
@ -56,11 +35,10 @@
<td id="map-container">
<div id="map"></div>
<div id="log"></div>
<div class="action-panel" id="action-panel"></div>
<div class="unit-info-panel" id="unit-info-panel"></div>
<div class="unit-control-panel" id="unit-control-panel"></div>
<div class="formation-control-panel" id="formation-control-panel"></div>
<div id="snackbar">ASD</div>
<div id="snackbar"></div>
</td>
</tr>
</table>

File diff suppressed because one or more lines are too long

View File

@ -1,43 +0,0 @@
class SelectionScroll
{
constructor(x, y, options, callback)
{
if (options.length > 1)
{
this._x = x;
this._y = y;
this._options = options;
/* Create the container of the scroll */
this._container = document.createElement("div");
this._container.id = 'selection-scroll-container';
this._container.style.left = this._x + "px";
this._container.style.top = this._y + "px";
document.getElementById("map-container").appendChild(this._container);
for (let optionID in this._options)
{
var node = document.createElement("div");
node.classList.add("selection-scroll-element");
node.appendChild(document.createTextNode(this._options[optionID]));
this._container.appendChild(node);
node.addEventListener('click', () => callback(this._options[optionID]))
}
window.setTimeout(() => this._show(), 100);
}
}
remove()
{
document.getElementById("map-container").removeChild(this._container);
}
_show()
{
this._container.style.width = 220 + "px";
this._container.style.height = 220 + "px";
this._container.style.left = this._x - 110 + "px";
this._container.style.top = this._y - 110 + "px";
}
}

View File

@ -1,100 +0,0 @@
class SelectionWheel
{
constructor(x, y, options)
{
if (options.length > 1)
{
this._x = x;
this._y = y;
this._options = options;
this._angularSize = 360 / this._options.length;
/* Create the container of the wheel */
this._container = document.createElement("div");
this._container.id = 'selection-wheel-container';
this._container.style.left = this._x + "px";
this._container.style.top = this._y + "px";
document.getElementById("map-container").appendChild(this._container);
/* Create the wheel itself */
this._wheel = document.createElement("div");
this._wheel.id = 'selection-wheel';
this._container.appendChild(this._wheel);
/* Create the buttons */
this._buttons = [];
for (let id in this._options)
{
var button = document.createElement("div");
button.classList.add("selection-wheel-button");
button.style.left = this._x - 25 + "px";
button.style.top = this._y - 25 + "px";
button.addEventListener('click', (e) => this._options[id].callback(e));
this._container.appendChild(button);
this._buttons.push(button);
var image = document.createElement("img");
image.classList.add("selection-wheel-image");
image.src = `img/buttons/${this._options[id].src}`
image.title = this._options[id].tooltip;
if ('tint' in this._options[id])
{
button.style.setProperty('background-color', this._options[id].tint);
image.style.opacity = 0;
}
button.appendChild(image);
}
/* Show the coalition switch if requested */
this._switchLabel = document.createElement("label");
this._switchLabel.classList.add("switch");
this._switchLabel.innerHTML = `<input type="checkbox" id="coalition-switch"> <span class="slider round"></span>`
this._container.appendChild(this._switchLabel);
document.getElementById("coalition-switch").addEventListener('change', (e) => this._onSwitch(e))
if (map.getActiveCoalition() == "red")
{
document.getElementById("coalition-switch").checked = true;
}
window.setTimeout(() => this._show(), 100);
}
}
remove()
{
this._container.removeChild(this._wheel);
document.getElementById("map-container").removeChild(this._container);
}
_show()
{
this._container.style.width = 220 + "px";
this._container.style.height = 220 + "px";
this._container.style.left = this._x - 110 + "px";
this._container.style.top = this._y - 110 + "px";
var r = 80;
for (let id in this._buttons)
{
var angle = parseInt(id) * this._angularSize;
this._buttons[id].style.opacity = 1;
this._buttons[id].style.left = this._x + r * Math.sin(deg2rad(angle)) - 25 + "px";
this._buttons[id].style.top = this._y - r * Math.cos(deg2rad(angle)) - 25 + "px";
}
this._switchLabel.style.opacity = 1;
}
_onSwitch(e)
{
if (e.currentTarget.checked) {
document.documentElement.style.setProperty('--normal', getComputedStyle(this._container).getPropertyValue("--red"));
map.setActiveCoalition("red");
} else {
document.documentElement.style.setProperty('--normal', getComputedStyle(this._container).getPropertyValue("--blue"));
map.setActiveCoalition("blue");
}
}
}

View File

@ -1,67 +0,0 @@
class MissionData
{
constructor()
{
this._bullseye = undefined;
this._bullseyeMarker = undefined;
this._airbasesMarkers = {};
}
update(data)
{
this._bullseye = data.missionData.bullseye;
this._unitsData = data.missionData.unitsData;
this._airbases = data.missionData.airbases;
this._drawBullseye();
this._drawAirbases();
}
getUnitData(ID)
{
if (ID in this._unitsData)
{
return this._unitsData[ID];
}
else
{
return undefined;
}
}
_drawBullseye()
{
if (this._bullseyeMarker === undefined)
{
this._bullseyeMarker = new L.Marker([this._bullseye.lat, this._bullseye.lng]).addTo(map.getMap());
}
else
{
this._bullseyeMarker.setLatLng(new L.LatLng(this._bullseye.lat, this._bullseye.lng));
}
}
_drawAirbases()
{
for (let idx in this._airbases)
{
var airbase = this._airbases[idx]
if (this._airbasesMarkers[idx] === undefined)
{
this._airbasesMarkers[idx] = new L.Marker.AirbaseMarker(new L.LatLng(airbase.lat, airbase.lng), {name: airbase.callsign}).addTo(map.getMap());
this._airbasesMarkers[idx].on('click', (e) => this._onAirbaseClick(e));
}
else
{
this._airbasesMarkers[idx].setCoalitionID(airbase.coalition);
this._airbasesMarkers[idx].setLatLng(new L.LatLng(airbase.lat, airbase.lng));
}
}
}
_onAirbaseClick(e)
{
e.airbaseName = e.sourceTarget.options.name;
e.coalitionID = e.sourceTarget.coalitionID;
map.spawnFromAirbase(e);
}
}

View File

@ -1,30 +0,0 @@
class ActionPanel
{
constructor(id)
{
this._panel = document.getElementById(id);
this._attackButton = new PanelButton(this._panel, "img/buttons/attack.png", "Attack unit");
this._bombButton = new PanelButton(this._panel, "img/buttons/bomb.png", "Precision bombing");
this._carpetButton = new PanelButton(this._panel, "img/buttons/carpet.png", "Carpet bombing");
this._landButton = new PanelButton(this._panel, "img/buttons/land.png", "Land here");
this._formationButton = new PanelButton(this._panel, "img/buttons/formation.png", "Create formation");
this._attackButton.addCallback(() => map.setState("ATTACK"));
this._bombButton.addCallback(() => map.setState("BOMB"));
this._carpetButton.addCallback(() => map.setState("CARPET_BOMB"));
this._landButton.addCallback(() => map.setState("LAND"));
this._formationButton.addCallback(() => map.setState("FORMATION"));
this.setEnabled(false);
}
setEnabled(enabled)
{
this._attackButton.setEnabled(enabled);
this._bombButton.setEnabled(false);
this._carpetButton.setEnabled(false);
this._landButton.setEnabled(false);
this._formationButton.setEnabled(enabled);
}
}

View File

@ -1,82 +0,0 @@
class PanelButton
{
constructor(parent, icon, tooltip)
{
this._div = document.createElement("div");
this.setIcon(icon, tooltip);
this.setSlashed(false);
this._div.classList.add("panel-button");
parent.appendChild(this._div);
this.setEnabled(true);
this._div.onclick = () => this._onClick();
this._callbacks = [];
}
setEnabled(enabled)
{
this._enabled = enabled;
if (enabled)
{
this._div.classList.remove("panel-button-disabled");
}
else
{
this._div.classList.add("panel-button-disabled");
}
}
addCallback(callback)
{
this._callbacks.push(callback);
}
clearCallbacks()
{
this._callbacks = [];
}
setIcon(icon, tooltip)
{
if (icon.includes("png"))
{
this._baseIcon = `<img src="${icon}" title="${tooltip}">`;
}
else
{
this._baseIcon = `<i class="fa ${icon}" title="${tooltip}"></i>`;
}
this._div.innerHTML = this._baseIcon;
}
setSubicon(subicon)
{
this._baseIcon = `<div style="display: flex;">${this._baseIcon}<i style="font-size: 10px;" class="fa ${subicon}"></i></div>`;
this._div.innerHTML = this._baseIcon;
}
setSlashed(slashed)
{
if (slashed)
{
this._div.innerHTML = `<div style="display: flex; justify-content: center;">${this._baseIcon}<i style="position:fixed;" class="fa fa-slash"></i></div>`;
}
else
{
this._div.innerHTML = this._baseIcon;
}
}
_onClick()
{
if (this._enabled)
{
for (let callback in this._callbacks)
{
this._callbacks[callback]();
}
}
}
}

View File

@ -1,86 +0,0 @@
class SettingsPanel
{
constructor(id)
{
this._panel = document.getElementById(id);
/* Create all buttons, disabled by default */
this._humanIcon = "fa-user";
this._AIIcon = "fa-desktop";
this._weaponsIcon = "fa-bomb";
this._labelsIcon = "fa-font";
this._deadIcon = "fa-skull";
this._humanButton = new PanelButton(this._panel, this._humanIcon, "Player visibility");
this._AIButton = new PanelButton(this._panel, this._AIIcon, "AI visibility");
this._weaponsButton = new PanelButton(this._panel, this._weaponsIcon, "Weapons visibility");
this._deadAliveButton = new PanelButton(this._panel, this._deadIcon, "Dead units visibility");
this._humanButton.addCallback(() => this._onHumanButton());
this._AIButton.addCallback(() => this._onAIButton());
this._weaponsButton.addCallback(() => this._onWeaponsButton());
this._deadAliveButton.addCallback(() => this._cycleDeadAlive());
this._human = "labels";
this._humanButton.setSubicon(this._labelsIcon);
this._AI = "marker";
this._weapons = "marker";
this._deadAlive = "both";
}
getSettings()
{
return {'human': this._human, 'AI': this._AI, 'weapons': this._weapons, 'deadAlive': this._deadAlive}
}
_onHumanButton()
{
this._human = this._cycleVisibility(this._humanButton, this._human, this._humanIcon);
}
_onAIButton()
{
this._AI = this._cycleVisibility(this._AIButton, this._AI, this._AIIcon);
}
_onWeaponsButton()
{
this._weapons = this._cycleVisibility(this._weaponsButton, this._weapons, this._weaponsIcon);
}
_cycleVisibility(button, variable, icon)
{
if (variable === "labels")
{
variable = "marker";
button.setIcon(icon);
button.setSlashed(false);
}
else if (variable === "marker")
{
variable = "none";
button.setSlashed(true);
}
else
{
variable = "labels";
button.setSubicon(this._labelsIcon);
button.setSlashed(false);
}
return variable;
}
_cycleDeadAlive()
{
if (this._deadAlive === "both")
{
this._deadAlive = "alive";
this._deadAliveButton.setSlashed(true);
}
else
{
this._deadAlive = "both";
this._deadAliveButton.setSlashed(false);
}
}
}

View File

@ -1,35 +0,0 @@
class UnitControlPanel
{
constructor(id)
{
this._panel = document.getElementById(id);
/* Create all buttons, disabled by default */
//this._moveButton = new PanelButton(this._panel, "fa-play");
//this._stopButton = new PanelButton(this._panel, "fa-pause");
this._slowButton = new PanelButton(this._panel, "fa-angle-right", "Decelerate");
this._fastButton = new PanelButton(this._panel, "fa-angle-double-right", "Accelerate");
this._descendButton = new PanelButton(this._panel, "fa-arrow-down", "Descend");
this._climbButton = new PanelButton(this._panel, "fa-arrow-up", "Climb");
//this._repeatButton = new PanelButton(this._panel, "fa-undo");
this.setEnabled(false);
//this._moveButton.addCallback(unitsManager.selectedUnitsMove);
//this._stopButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('stop'));
this._slowButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('slow'));
this._fastButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('fast'));
this._descendButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('descend'));
this._climbButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('climb'));
}
setEnabled(enabled)
{
//this._moveButton.setEnabled(true);
//this._stopButton.setEnabled(true);
this._slowButton.setEnabled(enabled);
this._fastButton.setEnabled(enabled);
this._descendButton.setEnabled(enabled);
this._climbButton.setEnabled(enabled);
}
}

1524
www/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
www/package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "olympus",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npm run build && npm run dev",
"build": "npm run clean && npm run mkdir && npm run build:html && npm run build:css && npm run build:js",
"dev": "webpack-dev-server --inline --hot --content-base build --history-api-fallback"
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/leaflet": "^1.9.0",
"express": "^4.18.2",
"leaflet": "^1.9.3"
},
"devDependencies": {
"@types/express": "^4.17.15",
"@types/node": "^18.11.18",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
}
}

View File

@ -1,4 +1,6 @@
function spawnSmoke(color, latlng)
import { ConvertDDToDMS } from 'Other/Utils.js'
export function spawnSmoke(color, latlng)
{
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
@ -15,7 +17,7 @@ function spawnSmoke(color, latlng)
xhr.send(JSON.stringify(data));
}
function spawnGroundUnit(type, latlng, coalition)
export function spawnGroundUnit(type, latlng, coalition)
{
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
@ -32,7 +34,7 @@ function spawnGroundUnit(type, latlng, coalition)
xhr.send(JSON.stringify(data));
}
function spawnAircraft(type, latlng, coalition, payloadName = "", airbaseName = "")
export function spawnAircraft(type, latlng, coalition, payloadName = "", airbaseName = "")
{
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
@ -49,7 +51,7 @@ function spawnAircraft(type, latlng, coalition, payloadName = "", airbaseName =
xhr.send(JSON.stringify(data));
}
function attackUnit(ID, targetID)
export function attackUnit(ID, targetID)
{
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
@ -66,7 +68,7 @@ function attackUnit(ID, targetID)
xhr.send(JSON.stringify(data));
}
function cloneUnit(ID)
export function cloneUnit(ID)
{
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);

File diff suppressed because one or more lines are too long

View File

@ -1,88 +1,106 @@
class Map
import { map, tileLayer } from 'leaflet'
import { SelectionWheel } from './SelectionWheel.js';
import { SelectionScroll } from './SelectionScroll.js';
import { spawnAircraft, spawnGroundUnit, spawnSmoke } from '../DCS/DCSCommands.js';
import { payloadNames } from '../DCS/payloadNames.js';
export class Map
{
#state : string;
#map : any; // TODO Fix, has same name of global variable
#selectionWheel : SelectionWheel;
#selectionScroll: SelectionScroll;
#activeCoalition: string;
constructor()
{
this._state = "IDLE";
this.#state = "IDLE";
this._map = L.map('map', {doubleClickZoom: false}).setView([37.23, -115.8], 12);
this.#map = map('map', {doubleClickZoom: false}).setView([37.23, -115.8], 12);
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
}).addTo(this._map);
}).addTo(this.#map);
// Register event handles
this._map.on('contextmenu', (e) => this._onContextMenu(e));
this._map.on('click', (e) => this._onClick(e));
this._map.on('dblclick', (e) => this._onDoubleClick(e));
this._map.on('movestart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();});
this._map.on('zoomstart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();});
this._map.on('selectionend', (e) => unitsManager.selectFromBounds(e.selectionBounds));
this._map.on('keyup', (e) => unitsManager.handleKeyEvent(e));
this.#map.on('contextmenu', (e) => this.#onContextMenu(e));
this.#map.on('click', (e) => this.#onClick(e));
this.#map.on('dblclick', (e) => this.#onDoubleClick(e));
this.#map.on('movestart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();});
this.#map.on('zoomstart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();});
this.#map.on('selectionend', (e) => unitsManager.selectFromBounds(e.selectionBounds));
this.#map.on('keyup', (e) => unitsManager.handleKeyEvent(e));
this._map._container.classList.add("action-cursor");
this.#map._container.classList.add("action-cursor");
this.setState("IDLE");
this._selectionWheel = undefined;
this._selectionScroll = undefined;
this.#selectionWheel = undefined;
this.#selectionScroll = undefined;
/* Edit the default zoom box effect to use it as a multiple units selection */
L.Map.BoxZoom.prototype._onMouseUp = function (e) {
// TODO
/* Edit the default zoom box effect to use it as a multiple units selection
Map.BoxZoom.prototype._onMouseUp = function (e) {
if ((e.which !== 1) && (e.button !== 1)) { return; }
this._finish();
this.#finish();
if (!this._moved) { return; }
if (!this.#moved) { return; }
// Postpone to next JS tick so internal click event handling
// still see it as "moved".
setTimeout(L.bind(this._resetState, this), 0);
setTimeout(L.bind(this.#resetState, this), 0);
var bounds = new L.LatLngBounds(
this._map.containerPointToLatLng(this._startPoint),
this._map.containerPointToLatLng(this._point));
this.#map.containerPointToLatLng(this.#startPoint),
this.#map.containerPointToLatLng(this.#point));
this._map.fire('selectionend', {selectionBounds: bounds});
this.#map.fire('selectionend', {selectionBounds: bounds});
}
*/
this._activeCoalition = "blue";
this.#activeCoalition = "blue";
}
getMap()
{
return this._map;
return this.#map;
}
/* State machine */
setState(newState)
{
this._state = newState;
this.#state = newState;
var cursorElements = document.getElementsByClassName("action-cursor");
for (let item of cursorElements)
for (let idx in cursorElements)
{
var item = cursorElements[idx];
item.classList.remove("move-cursor-enabled", "attack-cursor-enabled", "formation-cursor-enabled");
}
if (this._state === "IDLE")
if (this.#state === "IDLE")
{
}
else if (this._state === "MOVE_UNIT")
else if (this.#state === "MOVE_UNIT")
{
for (let item of cursorElements)
for (let idx in cursorElements)
{
var item = cursorElements[idx];
item.classList.add("move-cursor-enabled");
}
}
else if (this._state === "ATTACK")
else if (this.#state === "ATTACK")
{
for (let item of cursorElements)
for (let idx in cursorElements)
{
var item = cursorElements[idx];
item.classList.add("attack-cursor-enabled");
}
}
else if (this._state === "FORMATION")
else if (this.#state === "FORMATION")
{
for (let item of cursorElements)
for (let idx in cursorElements)
{
var item = cursorElements[idx];
item.classList.add("formation-cursor-enabled");
}
}
@ -90,23 +108,23 @@ class Map
getState()
{
return this._state;
return this.#state;
}
/* Set the active coalition (for persistency) */
setActiveCoalition(coalition)
{
this._activeCoalition = coalition;
this.#activeCoalition = coalition;
}
getActiveCoalition()
{
return this._activeCoalition;
return this.#activeCoalition;
}
/* Event handlers */
// Right click
_onContextMenu(e)
#onContextMenu(e)
{
this.setState("IDLE");
unitsManager.deselectAllUnits();
@ -114,15 +132,15 @@ class Map
this.removeSelectionScroll();
}
_onClick(e)
#onClick(e)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
if (this._state === "IDLE")
if (this.#state === "IDLE")
{
}
else if (this._state === "MOVE_UNIT")
else if (this.#state === "MOVE_UNIT")
{
if (!e.originalEvent.ctrlKey)
{
@ -132,81 +150,84 @@ class Map
}
}
_onDoubleClick(e)
#onDoubleClick(e)
{
if (this._state == 'IDLE')
if (this.#state == 'IDLE')
{
var options = [
{'tooltip': 'Air unit', 'src': 'spawnAir.png', 'callback': () => this._aircraftSpawnMenu(e)},
{'tooltip': 'Ground unit', 'src': 'spawnGround.png', 'callback': () => this._groundUnitSpawnMenu(e)},
{'tooltip': 'Smoke', 'src': 'spawnSmoke.png', 'callback': () => this._smokeSpawnMenu(e)},
{'tooltip': 'Explosion', 'src': 'spawnExplosion.png', 'callback': () => this._explosionSpawnMenu(e)}
{'tooltip': 'Air unit', 'src': 'spawnAir.png', 'callback': () => this.#aircraftSpawnMenu(e)},
{'tooltip': 'Ground unit', 'src': 'spawnGround.png', 'callback': () => this.#groundUnitSpawnMenu(e)},
{'tooltip': 'Smoke', 'src': 'spawnSmoke.png', 'callback': () => this.#smokeSpawnMenu(e)},
{'tooltip': 'Explosion', 'src': 'spawnExplosion.png', 'callback': () => this.#explosionSpawnMenu(e)}
]
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
this.showSelectionWheel(e, options, true);
}
}
showSelectionWheel(e, options, coalition)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
this.#selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options, coalition);
}
/* Selection wheel and selection scroll functions */
removeSelectionWheel()
{
if (this._selectionWheel !== undefined)
if (this.#selectionWheel !== undefined)
{
this._selectionWheel.remove();
this._selectionWheel = undefined;
this.#selectionWheel.remove();
this.#selectionWheel = undefined;
}
}
removeSelectionScroll()
{
if (this._selectionScroll !== undefined)
if (this.#selectionScroll !== undefined)
{
this._selectionScroll.remove();
this._selectionScroll = undefined;
this.#selectionScroll.remove();
this.#selectionScroll = undefined;
}
}
/* Show unit selection for air units */
spawnFromAirbase(e)
{
this._selectAircraft(e);
this.#selectAircraft(e, undefined);
}
/* Spawn a new ground unit selection wheel */
_aircraftSpawnMenu(e)
#aircraftSpawnMenu(e)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
var options = [
{'coalition': true, 'tooltip': 'CAP', 'src': 'spawnCAP.png', 'callback': () => this._selectAircraft(e, "CAP")},
{'coalition': true, 'tooltip': 'CAS', 'src': 'spawnCAS.png', 'callback': () => this._selectAircraft(e, "CAS")},
{'coalition': true, 'tooltip': 'Tanker', 'src': 'spawnTanker.png', 'callback': () => this._selectAircraft(e, "tanker")},
{'coalition': true, 'tooltip': 'AWACS', 'src': 'spawnAWACS.png', 'callback': () => this._selectAircraft(e, "awacs")},
{'coalition': true, 'tooltip': 'Strike', 'src': 'spawnStrike.png', 'callback': () => this._selectAircraft(e, "strike")},
{'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this._selectAircraft(e, "drone")},
{'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png','callback': () => this._selectAircraft(e, "transport")},
{'coalition': true, 'tooltip': 'CAP', 'src': 'spawnCAP.png', 'callback': () => this.#selectAircraft(e, "CAP")},
{'coalition': true, 'tooltip': 'CAS', 'src': 'spawnCAS.png', 'callback': () => this.#selectAircraft(e, "CAS")},
{'coalition': true, 'tooltip': 'Tanker', 'src': 'spawnTanker.png', 'callback': () => this.#selectAircraft(e, "tanker")},
{'coalition': true, 'tooltip': 'AWACS', 'src': 'spawnAWACS.png', 'callback': () => this.#selectAircraft(e, "awacs")},
{'coalition': true, 'tooltip': 'Strike', 'src': 'spawnStrike.png', 'callback': () => this.#selectAircraft(e, "strike")},
{'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this.#selectAircraft(e, "drone")},
{'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png','callback': () => this.#selectAircraft(e, "transport")},
]
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
this.showSelectionWheel(e, options, true);
}
/* Spawn a new ground unit selection wheel */
_groundUnitSpawnMenu(e)
#groundUnitSpawnMenu(e)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
var options = [
{'coalition': true, 'tooltip': 'Howitzer', 'src': 'spawnHowitzer.png', 'callback': () => this._selectGroundUnit(e, "Howitzers")},
{'coalition': true, 'tooltip': 'SAM', 'src': 'spawnSAM.png', 'callback': () => this._selectGroundUnit(e, "SAM")},
{'coalition': true, 'tooltip': 'IFV', 'src': 'spawnIFV.png', 'callback': () => this._selectGroundUnit(e, "IFV")},
{'coalition': true, 'tooltip': 'Tank', 'src': 'spawnTank.png', 'callback': () => this._selectGroundUnit(e, "Tanks")},
{'coalition': true, 'tooltip': 'MLRS', 'src': 'spawnMLRS.png', 'callback': () => this._selectGroundUnit(e, "MLRS")},
{'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this._selectGroundUnit(e, "Radar")},
{'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this._selectGroundUnit(e, "Unarmed")}
{'coalition': true, 'tooltip': 'Howitzer', 'src': 'spawnHowitzer.png', 'callback': () => this.#selectGroundUnit(e, "Howitzers")},
{'coalition': true, 'tooltip': 'SAM', 'src': 'spawnSAM.png', 'callback': () => this.#selectGroundUnit(e, "SAM")},
{'coalition': true, 'tooltip': 'IFV', 'src': 'spawnIFV.png', 'callback': () => this.#selectGroundUnit(e, "IFV")},
{'coalition': true, 'tooltip': 'Tank', 'src': 'spawnTank.png', 'callback': () => this.#selectGroundUnit(e, "Tanks")},
{'coalition': true, 'tooltip': 'MLRS', 'src': 'spawnMLRS.png', 'callback': () => this.#selectGroundUnit(e, "MLRS")},
{'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")},
{'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")}
]
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
this.showSelectionWheel(e, options, true);
}
/* Spawn smoke selection wheel */
_smokeSpawnMenu(e)
#smokeSpawnMenu(e)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
@ -217,36 +238,36 @@ class Map
{'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); spawnSmoke('green', e.latlng)}, 'tint': 'green'},
{'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'},
]
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
this.showSelectionWheel(e, options, true);
}
/* Spawn an explosion selection wheel (TODO) */
_explosionSpawnMenu(e)
#explosionSpawnMenu(e)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
var options = [
]
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
this.showSelectionWheel(e, options, true);
}
/* Show unit selection for air units */
_selectAircraft(e, group)
#selectAircraft(e, group)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
var options = unitTypes.air[group];
options.sort();
this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (unitType) => {
this.#selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (unitType) => {
this.removeSelectionWheel();
this.removeSelectionScroll();
this._unitSelectPayload(unitType, e);
this.#unitSelectPayload(unitType, e);
});
}
/* Show weapon selection for air units */
_unitSelectPayload(unitType, e)
#unitSelectPayload(unitType, e)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
@ -255,29 +276,29 @@ class Map
if (options != undefined && options.length > 0)
{
options.sort();
this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (payloadName) => {
this.#selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (payloadName) => {
this.removeSelectionWheel();
this.removeSelectionScroll();
spawnAircraft(unitType, e.latlng, this._activeCoalition, payloadName, e.airbaseName);
spawnAircraft(unitType, e.latlng, this.#activeCoalition, payloadName, e.airbaseName);
});
}
else
{
spawnAircraft(unitType, e.latlng, this._activeCoalition);
spawnAircraft(unitType, e.latlng, this.#activeCoalition);
}
}
/* Show unit selection for ground units */
_selectGroundUnit(e, group)
#selectGroundUnit(e, group)
{
this.removeSelectionWheel();
this.removeSelectionScroll();
var options = unitTypes.vehicles[group];
options.sort();
this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (type) => {
this.#selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (type) => {
this.removeSelectionWheel();
this.removeSelectionScroll();
spawnGroundUnit(type, e.latlng, this._activeCoalition);
spawnGroundUnit(type, e.latlng, this.#activeCoalition);
});
}
}

View File

@ -0,0 +1,48 @@
export class SelectionScroll
{
#x : number;
#y : number;
#options : any; // TODO declare interface
#container : HTMLElement;
constructor(x, y, options, callback)
{
if (options.length > 1)
{
this.#x = x;
this.#y = y;
this.#options = options;
/* Create the container of the scroll */
this.#container = document.createElement("div");
this.#container.id = 'selection-scroll-container';
this.#container.style.left = this.#x + "px";
this.#container.style.top = this.#y + "px";
document.getElementById("map-container").appendChild(this.#container);
for (let optionID in this.#options)
{
var node = document.createElement("div");
node.classList.add("selection-scroll-element");
node.appendChild(document.createTextNode(this.#options[optionID]));
this.#container.appendChild(node);
node.addEventListener('click', () => callback(this.#options[optionID]))
}
window.setTimeout(() => this.#show(), 100);
}
}
remove()
{
document.getElementById("map-container").removeChild(this.#container);
}
#show()
{
this.#container.style.width = 220 + "px";
this.#container.style.height = 220 + "px";
this.#container.style.left = this.#x - 110 + "px";
this.#container.style.top = this.#y - 110 + "px";
}
}

View File

@ -0,0 +1,130 @@
import { deg2rad } from 'Other/Utils.js'
export class SelectionWheel
{
#x : number;
#y : number;
#options : any; // TODO declare interface
#angularSize : number;
#container : HTMLElement;
#wheel : HTMLElement;
#buttons : HTMLElement[];
#switchLabel : HTMLLabelElement;
constructor(x, y, options, coalition)
{
if (options.length > 1)
{
this.#x = x;
this.#y = y;
this.#options = options;
this.#angularSize = 360 / this.#options.length;
/* Create the container of the wheel */
this.#container = document.createElement("div");
this.#container.id = 'selection-wheel-container';
this.#container.style.left = this.#x + "px";
this.#container.style.top = this.#y + "px";
document.getElementById("map-container").appendChild(this.#container);
/* Create the wheel itself */
this.#wheel = document.createElement("div");
this.#wheel.id = 'selection-wheel';
this.#container.appendChild(this.#wheel);
/* Create the buttons */
this.#buttons = [];
for (let id in this.#options)
{
var button = document.createElement("div");
button.classList.add("selection-wheel-button");
button.style.left = this.#x - 25 + "px";
button.style.top = this.#y - 25 + "px";
button.addEventListener('click', (e) => this.#options[id].callback(e));
this.#container.appendChild(button);
this.#buttons.push(button);
var image = document.createElement("img");
image.classList.add("selection-wheel-image");
image.src = `img/buttons/${this.#options[id].src}`
image.title = this.#options[id].tooltip;
if ('tint' in this.#options[id])
{
button.style.setProperty('background-color', this.#options[id].tint);
image.style.opacity = "0";
}
button.appendChild(image);
}
/* Show the coalition switch if requested */
if (coalition)
{
this.#switchLabel = <HTMLLabelElement>document.createElement("label");
this.#switchLabel.classList.add("switch");
this.#switchLabel.innerHTML = `<input type="checkbox" id="coalition-switch"> <span class="slider round"></span>`
this.#container.appendChild(this.#switchLabel);
document.getElementById("coalition-switch").addEventListener('change', (e) => this.#onSwitch(e))
if (map.getActiveCoalition() == "red")
{
document.documentElement.style.setProperty('--normal', getComputedStyle(this.#container).getPropertyValue("--red"));
(<HTMLInputElement>document.getElementById("coalition-switch")).checked = true;
}
else
{
document.documentElement.style.setProperty('--normal', getComputedStyle(this.#container).getPropertyValue("--blue"));
}
}
else
{
document.documentElement.style.setProperty('--normal', getComputedStyle(this.#container).getPropertyValue("--dark"));
}
window.setTimeout(() => this.#show(), 100);
}
}
remove()
{
if (this.#container != undefined)
{
this.#container.removeChild(this.#wheel);
document.getElementById("map-container").removeChild(this.#container);
}
}
#show()
{
this.#container.style.width = 220 + "px";
this.#container.style.height = 220 + "px";
this.#container.style.left = this.#x - 110 + "px";
this.#container.style.top = this.#y - 110 + "px";
var r = 80;
for (let id in this.#buttons)
{
var angle = parseInt(id) * this.#angularSize;
this.#buttons[id].style.opacity = "1";
this.#buttons[id].style.left = this.#x + r * Math.sin(deg2rad(angle)) - 25 + "px";
this.#buttons[id].style.top = this.#y - r * Math.cos(deg2rad(angle)) - 25 + "px";
}
if (this.#switchLabel != undefined)
{
this.#switchLabel.style.opacity = "1";
}
}
#onSwitch(e)
{
if (e.currentTarget.checked) {
document.documentElement.style.setProperty('--normal', getComputedStyle(this.#container).getPropertyValue("--red"));
map.setActiveCoalition("red");
} else {
document.documentElement.style.setProperty('--normal', getComputedStyle(this.#container).getPropertyValue("--blue"));
map.setActiveCoalition("blue");
}
}
}

View File

@ -0,0 +1,77 @@
import { Marker, LatLng } from "leaflet";
export class MissionData
{
#bullseye : any; //TODO declare interface
#bullseyeMarker : Marker;
#airbasesMarkers: {[name: string]: Marker};
#unitsData : any; //TODO declare interface
#airbases : any; //TODO declare interface
constructor()
{
this.#bullseye = undefined;
this.#bullseyeMarker = undefined;
this.#airbasesMarkers = {};
}
update(data)
{
this.#bullseye = data.missionData.bullseye;
this.#unitsData = data.missionData.unitsData;
this.#airbases = data.missionData.airbases;
this.#drawBullseye();
this.#drawAirbases();
}
getUnitData(ID)
{
if (ID in this.#unitsData)
{
return this.#unitsData[ID];
}
else
{
return undefined;
}
}
#drawBullseye()
{
if (this.#bullseyeMarker === undefined)
{
this.#bullseyeMarker = new Marker([this.#bullseye.lat, this.#bullseye.lng]).addTo(map.getMap());
}
else
{
this.#bullseyeMarker.setLatLng(new LatLng(this.#bullseye.lat, this.#bullseye.lng));
}
}
#drawAirbases()
{
for (let idx in this.#airbases)
{
var airbase = this.#airbases[idx]
if (this.#airbasesMarkers[idx] === undefined)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
this.#airbasesMarkers[idx] = new L.Marker.AirbaseMarker(new L.LatLng(airbase.lat, airbase.lng), {name: airbase.callsign}).addTo(map.getMap());
this.#airbasesMarkers[idx].on('click', (e) => this.#onAirbaseClick(e));
}
else
{
// @ts-ignore TODO: find a good way to extend markers in typescript
this.#airbasesMarkers[idx].setCoalitionID(airbase.coalition);
this.#airbasesMarkers[idx].setLatLng(new LatLng(airbase.lat, airbase.lng));
}
}
}
#onAirbaseClick(e)
{
e.airbaseName = e.sourceTarget.options.name;
e.coalitionID = e.sourceTarget.coalitionID;
map.spawnFromAirbase(e);
}
}

View File

@ -1,4 +1,4 @@
function distance(lat1, lon1, lat2, lon2)
export function distance(lat1, lon1, lat2, lon2)
{
const R = 6371e3; // metres
const φ1 = deg2rad(lat1); // φ, λ in radians
@ -14,7 +14,7 @@ function distance(lat1, lon1, lat2, lon2)
return d;
}
function bearing(lat1, lon1, lat2, lon2)
export function bearing(lat1, lon1, lat2, lon2)
{
const φ1 = deg2rad(lat1); // φ, λ in radians
const φ2 = deg2rad(lat2);
@ -28,49 +28,9 @@ function bearing(lat1, lon1, lat2, lon2)
return brng;
}
function latlng2xy(lat, lon)
{
const latBulls = missionData.bullseye.lat;
const lonBulls = missionData.bullseye.lng;
const d = distance(latBulls, lonBulls, lat, lon);
const brng = bearing(latBulls, lonBulls, lat, lon);
const x = d * Math.cos(deg2rad(brng));
const y = d * Math.sin(deg2rad(brng));
xy = {};
xy.x = x;
xy.y = y;
return xy;
}
function xy2latlng(x, y)
{
const xBulls = missionData.bullseye.x;
const yBulls = missionData.bullseye.y;
const latBulls = missionData.bullseye.lat;
const lonBulls = missionData.bullseye.lng;
const R = 6371e3; // metres
const φ1 = deg2rad(latBulls); // φ, λ in radians
const λ1 = deg2rad(lonBulls); // φ, λ in radians
const d = Math.sqrt(Math.pow(x - xBulls, 2) + Math.pow(y - yBulls, 2));
const brng = -rad2deg(Math.atan2(y - yBulls, x - xBulls));
const φ2 = Math.asin(Math.sin(φ1)*Math.cos(d/R) + Math.cos(φ1)*Math.sin(d/R)*Math.cos(brng));
const λ2 = λ1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(φ1), Math.cos(d/R)-Math.sin(φ1)*Math.sin(φ2));
latlng = {};
latlng.lat = rad2deg(φ2);
latlng.lng = rad2deg(λ2);
return latlng;
}
const zeroPad = (num, places) => String(num).padStart(places, '0')
function ConvertDDToDMS(D, lng)
export function ConvertDDToDMS(D, lng)
{
var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N";
var deg = 0 | (D < 0 ? (D = -D) : D);
@ -84,13 +44,13 @@ function ConvertDDToDMS(D, lng)
return dir + zeroPad(deg, 2) + "°" + zeroPad(min, 2) + "'" + zeroPad(sec, 2) + "." + zeroPad(dec, 2) + "\"";
}
function deg2rad(deg)
export function deg2rad(deg)
{
var pi = Math.PI;
return deg * (pi/180);
}
function rad2deg(rad)
export function rad2deg(rad)
{
var pi = Math.PI;
return rad / (pi/180);

View File

@ -1,10 +1,14 @@
class FormationControlPanel
export class FormationControlPanel
{
#panel : HTMLElement;
#formations : string[];
#editing : boolean;
constructor(id)
{
this._panel = document.getElementById(id);
this.#panel = document.getElementById(id);
this._formations = ["", "Echelon", "Line abreast", "Box", "Trail", "Finger tip", "Tactical line abreast", "Fluid four", "Spread four"];
this.#formations = ["", "Echelon", "Line abreast", "Box", "Trail", "Finger tip", "Tactical line abreast", "Fluid four", "Spread four"];
}
update(selectedUnits)
@ -12,24 +16,24 @@ class FormationControlPanel
if (selectedUnits.length == 1)
{
// Don't update if user is editing
if (selectedUnits[0].leader && !this._editing)
if (selectedUnits[0].leader && !this.#editing)
{
this._panel.style.bottom = "15px";
this._showFormationControls(selectedUnits[0]);
this.#panel.style.bottom = "15px";
this.#showFormationControls(selectedUnits[0]);
}
}
else
{
this._panel.style.bottom = (-this._panel.offsetHeight - 2) + "px";
this._showFormationControls(); // Empty, cleans the panel
this.#panel.style.bottom = (-this.#panel.offsetHeight - 2) + "px";
this.#showFormationControls(undefined); // Empty, cleans the panel
}
}
_showFormationControls(selectedUnit)
#showFormationControls(selectedUnit)
{
if (selectedUnit !== undefined)
{
this._panel.innerHTML = `
this.#panel.innerHTML = `
<div style="display: flex">
<table class="panel-table" id="unit-info-table">
<tr>
@ -57,24 +61,24 @@ class FormationControlPanel
</div>
`;
var select = document.getElementById("formation-type-select");
for(var i = 0; i < this._formations.length; i++) {
var opt = this._formations[i];
var select: HTMLSelectElement = <HTMLSelectElement>document.getElementById("formation-type-select");
for(var i = 0; i < this.#formations.length; i++) {
var opt = this.#formations[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
select.addEventListener("focus", () => this._editing = true)
select.addEventListener("blur", () => this._editing = false)
object.addEventListener("change", () => leader.setformation());
select.addEventListener("focus", () => this.#editing = true)
select.addEventListener("blur", () => this.#editing = false)
//select.addEventListener("change", () => leader.setformation());
select.value = selectedUnit.formation;
}
else
{
this._panel.innerHTML = ``;
this.#panel.innerHTML = ``;
}
}
}

View File

@ -0,0 +1,87 @@
export class PanelButton
{
#div : HTMLElement;
#enabled : boolean;
#callbacks : any[]; // TODO how to set type callables?
#baseIconHTML : string;
constructor(parent, icon, tooltip)
{
this.#div = document.createElement("div");
this.setIcon(icon, tooltip);
this.setSlashed(false);
this.#div.classList.add("panel-button");
parent.appendChild(this.#div);
this.setEnabled(true);
this.#div.onclick = () => this.#onClick();
this.#callbacks = [];
}
setEnabled(enabled)
{
this.#enabled = enabled;
if (enabled)
{
this.#div.classList.remove("panel-button-disabled");
}
else
{
this.#div.classList.add("panel-button-disabled");
}
}
addCallback(callback)
{
this.#callbacks.push(callback);
}
clearCallbacks()
{
this.#callbacks = [];
}
setIcon(icon, tooltip)
{
if (icon.includes("png"))
{
this.#baseIconHTML = `<img src="${icon}" title="${tooltip}">`;
}
else
{
this.#baseIconHTML = `<i class="fa ${icon}" title="${tooltip}"></i>`;
}
this.#div.innerHTML = this.#baseIconHTML;
}
setSubicon(subicon)
{
this.#baseIconHTML = `<div style="display: flex;">${this.#baseIconHTML}<i style="font-size: 10px;" class="fa ${subicon}"></i></div>`;
this.#div.innerHTML = this.#baseIconHTML;
}
setSlashed(slashed)
{
if (slashed)
{
this.#div.innerHTML = `<div style="display: flex; justify-content: center;">${this.#baseIconHTML}<i style="position:fixed;" class="fa fa-slash"></i></div>`;
}
else
{
this.#div.innerHTML = this.#baseIconHTML;
}
}
#onClick()
{
if (this.#enabled)
{
for (let callback in this.#callbacks)
{
this.#callbacks[callback]();
}
}
}
}

View File

@ -0,0 +1,106 @@
import { PanelButton } from "./PanelButton.js";
export class SettingsPanel
{
#panel : HTMLElement;
#humanIcon : string;
#AIIcon : string;
#weaponsIcon : string;
#labelsIcon : string;
#deadIcon : string;
#humanButton : PanelButton;
#AIButton : PanelButton;
#weaponsButton : PanelButton;
#deadAliveButton : PanelButton;
#human : string;
#AI : string;
#weapons : string;
#deadAlive : string;
constructor(id)
{
this.#panel = document.getElementById(id);
/* Create all buttons, disabled by default */
this.#humanIcon = "fa-user";
this.#AIIcon = "fa-desktop";
this.#weaponsIcon = "fa-bomb";
this.#labelsIcon = "fa-font";
this.#deadIcon = "fa-skull";
this.#humanButton = new PanelButton(this.#panel, this.#humanIcon, "Player visibility");
this.#AIButton = new PanelButton(this.#panel, this.#AIIcon, "AI visibility");
this.#weaponsButton = new PanelButton(this.#panel, this.#weaponsIcon, "Weapons visibility");
this.#deadAliveButton = new PanelButton(this.#panel, this.#deadIcon, "Dead units visibility");
this.#humanButton.addCallback(() => this.#onHumanButton());
this.#AIButton.addCallback(() => this.#onAIButton());
this.#weaponsButton.addCallback(() => this.#onWeaponsButton());
this.#deadAliveButton.addCallback(() => this.#cycleDeadAlive());
this.#human = "labels";
this.#humanButton.setSubicon(this.#labelsIcon);
this.#AI = "marker";
this.#weapons = "marker";
this.#deadAlive = "both";
}
getSettings()
{
return {'human': this.#human, 'AI': this.#AI, 'weapons': this.#weapons, 'deadAlive': this.#deadAlive}
}
#onHumanButton()
{
this.#human = this.#cycleVisibility(this.#humanButton, this.#human, this.#humanIcon);
}
#onAIButton()
{
this.#AI = this.#cycleVisibility(this.#AIButton, this.#AI, this.#AIIcon);
}
#onWeaponsButton()
{
this.#weapons = this.#cycleVisibility(this.#weaponsButton, this.#weapons, this.#weaponsIcon);
}
#cycleVisibility(button, variable, icon)
{
if (variable === "labels")
{
variable = "marker";
button.setIcon(icon);
button.setSlashed(false);
}
else if (variable === "marker")
{
variable = "none";
button.setSlashed(true);
}
else
{
variable = "labels";
button.setSubicon(this.#labelsIcon);
button.setSlashed(false);
}
return variable;
}
#cycleDeadAlive()
{
if (this.#deadAlive === "both")
{
this.#deadAlive = "alive";
this.#deadAliveButton.setSlashed(true);
}
else
{
this.#deadAlive = "both";
this.#deadAliveButton.setSlashed(false);
}
}
}

View File

@ -0,0 +1,43 @@
import { PanelButton } from "./PanelButton.js";
export class UnitControlPanel
{
#panel : HTMLElement;
#slowButton : PanelButton;
#fastButton : PanelButton;
#descendButton : PanelButton;
#climbButton : PanelButton;
constructor(id)
{
this.#panel = document.getElementById(id);
/* Create all buttons, disabled by default */
//this.#moveButton = new PanelButton(this.#panel, "fa-play");
//this.#stopButton = new PanelButton(this.#panel, "fa-pause");
this.#slowButton = new PanelButton(this.#panel, "fa-angle-right", "Decelerate");
this.#fastButton = new PanelButton(this.#panel, "fa-angle-double-right", "Accelerate");
this.#descendButton = new PanelButton(this.#panel, "fa-arrow-down", "Descend");
this.#climbButton = new PanelButton(this.#panel, "fa-arrow-up", "Climb");
//this.#repeatButton = new PanelButton(this.#panel, "fa-undo");
this.setEnabled(false);
//this.#moveButton.addCallback(unitsManager.selectedUnitsMove);
//this.#stopButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('stop'));
this.#slowButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('slow'));
this.#fastButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('fast'));
this.#descendButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('descend'));
this.#climbButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('climb'));
}
setEnabled(enabled)
{
//this.#moveButton.setEnabled(true);
//this.#stopButton.setEnabled(true);
this.#slowButton.setEnabled(enabled);
this.#fastButton.setEnabled(enabled);
this.#descendButton.setEnabled(enabled);
this.#climbButton.setEnabled(enabled);
}
}

View File

@ -1,25 +1,29 @@
class UnitInfoPanel
import { ConvertDDToDMS, rad2deg } from 'Other/Utils.js'
export class UnitInfoPanel
{
#panel: HTMLElement;
constructor(id)
{
this._panel = document.getElementById(id);
this.#panel = document.getElementById(id);
}
update(selectedUnits)
{
if (selectedUnits.length == 1)
{
this._panel.style.bottom = "15px";
this._showUnitData(selectedUnits[0]);
this.#panel.style.bottom = "15px";
this.#showUnitData(selectedUnits[0]);
}
else
{
this._panel.style.bottom = (-this._panel.offsetHeight - 2) + "px";
this._showUnitData(); // Empty, cleans the panel
this.#panel.style.bottom = (-this.#panel.offsetHeight - 2) + "px";
this.#showUnitData(undefined); // Empty, cleans the panel
}
}
_showUnitData(selectedUnit)
#showUnitData(selectedUnit)
{
if (selectedUnit !== undefined)
{
@ -39,7 +43,7 @@ class UnitInfoPanel
}
}
this._panel.innerHTML = `
this.#panel.innerHTML = `
<div style="display: flex">
<table class="panel-table" id="unit-info-table">
<tr>
@ -135,7 +139,7 @@ class UnitInfoPanel
}
else
{
this._panel.innerHTML = ``;
this.#panel.innerHTML = ``;
}
}
}

View File

@ -1,26 +1,59 @@
class Unit
import { Marker, LatLng, Polyline } from 'leaflet';
import { ConvertDDToDMS } from 'Other/Utils.js'
import { showMessage } from 'index.js'
import { attackUnit } from 'DCS/DCSCommands.js'
export class Unit
{
ID : number;
selectable : boolean;
// @ts-ignore TODO: find a good way to extend markers in typescript
marker : L.Marker.UnitMarker;
leader : boolean;
wingman : boolean;
wingmen : Unit[];
formation : string;
name : string;
unitName : string;
groupName : string;
latitude : number;
longitude : number;
altitude : number;
heading : number;
coalitionID : number;
alive : boolean;
speed : number;
currentTask : string;
type : JSON;
flags : JSON;
activePath : JSON;
missionData : JSON;
#selected : boolean;
#preventClick : boolean;
#pathMarkers : Marker[];
#pathPolyline : Polyline;
#targetsPolylines : Polyline[];
#timer : number;
constructor(ID, marker)
{
this.ID = ID;
this.selectable = true;
// The marker is set by the inherited class
this.marker = marker;
this.marker.on('click', (e) => this.onClick(e));
this._selected = false;
this._pathMarkers = [];
this._pathPolyline = new L.Polyline([], {color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1});
this._pathPolyline.addTo(map.getMap());
this._targetsPolylines = [];
this.marker.on('click', (e) => this.#onClick(e));
this.marker.on('dblclick', (e) => this.#onDoubleClick(e));
this.leader = true;
this.wingmen = [];
this.formation = undefined;
this.#selected = false;
this.#preventClick = false;
this.#pathMarkers = [];
this.#pathPolyline = new Polyline([], {color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1});
this.#pathPolyline.addTo(map.getMap());
this.#targetsPolylines = [];
}
update(response)
@ -55,8 +88,8 @@ class Unit
this.missionData = missionData.getUnitData(this.ID)
this.setSelected(this.getSelected() & this.alive)
this.drawMarker();
this.setSelected(this.getSelected() && this.alive)
this.drawMarker({});
if (this.getSelected() && this.activePath != undefined)
{
this.drawPath();
@ -80,9 +113,9 @@ class Unit
setSelected(selected)
{
// Only alive units can be selected. Some units are not selectable (weapons)
if ((this.alive || !selected) && this.selectable && this._selected != selected)
if ((this.alive || !selected) && this.selectable && this.#selected != selected)
{
this._selected = selected;
this.#selected = selected;
this.marker.setSelected(selected);
unitsManager.onUnitSelection();
}
@ -90,7 +123,7 @@ class Unit
getSelected()
{
return this._selected;
return this.#selected;
}
addDestination(latlng)
@ -127,24 +160,40 @@ class Unit
this.activePath = undefined;
}
onClick(e)
#onClick(e)
{
if (map.getState() === 'IDLE' || map.getState() === 'MOVE_UNIT' || e.originalEvent.ctrlKey)
{
if (!e.originalEvent.ctrlKey)
{
unitsManager.deselectAllUnits();
this.#timer = setTimeout(() => {
if (!this.#preventClick) {
if (map.getState() === 'IDLE' || map.getState() === 'MOVE_UNIT' || e.originalEvent.ctrlKey)
{
if (!e.originalEvent.ctrlKey)
{
unitsManager.deselectAllUnits();
}
this.setSelected(true);
}
}
this.setSelected(true);
}
else if (map.getState() === 'ATTACK')
this.#preventClick = false;
}, 200);
}
#onDoubleClick(e)
{
clearTimeout(this.#timer);
this.#preventClick = true;
var options = [
{'tooltip': 'Attack', 'src': 'attack.png', 'callback': () => {map.removeSelectionWheel(); unitsManager.attackUnit(this.ID);}},
{'tooltip': 'Go to tanker', 'src': 'tanker.png', 'callback': () => {map.removeSelectionWheel(); showMessage("Function not implemented yet");}},
{'tooltip': 'RTB', 'src': 'rtb.png', 'callback': () => {map.removeSelectionWheel(); showMessage("Function not implemented yet");}}
]
if (!this.leader && !this.wingman)
{
unitsManager.attackUnit(this.ID);
}
else if (map.getState() === 'FORMATION')
{
unitsManager.createFormation(this.ID);
options.push({'tooltip': 'Create formation', 'src': 'formation.png', 'callback': () => {map.removeSelectionWheel(); unitsManager.createFormation(this.ID);}});
}
map.showSelectionWheel(e, options, false);
}
drawMarker(settings)
@ -170,7 +219,7 @@ class Unit
// Draw the marker
var zIndex = this.marker.getZIndex();
var newLatLng = new L.LatLng(this.latitude, this.longitude);
var newLatLng = new LatLng(this.latitude, this.longitude);
this.marker.setLatLng(newLatLng);
this.marker.setAngle(this.heading);
this.marker.setZIndex(zIndex);
@ -183,53 +232,53 @@ class Unit
drawPath()
{
var _points = [];
_points.push(new L.LatLng(this.latitude, this.longitude));
_points.push(new LatLng(this.latitude, this.longitude));
// Add markers if missing
while (this._pathMarkers.length < Object.keys(this.activePath).length)
while (this.#pathMarkers.length < Object.keys(this.activePath).length)
{
var marker = L.marker([0, 0]).addTo(map.getMap());
this._pathMarkers.push(marker);
var marker = new Marker([0, 0]).addTo(map.getMap());
this.#pathMarkers.push(marker);
}
// Remove markers if too many
while (this._pathMarkers.length > Object.keys(this.activePath).length)
while (this.#pathMarkers.length > Object.keys(this.activePath).length)
{
map.getMap().removeLayer(this._pathMarkers[this._pathMarkers.length - 1]);
this._pathMarkers.splice(this._pathMarkers.length - 1, 1)
map.getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1)
}
// Update the position of the existing markers (to avoid creating markers uselessly)
for (let WP in this.activePath)
{
var destination = this.activePath[WP];
this._pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]);
_points.push(new L.LatLng(destination.lat, destination.lng));
this._pathPolyline.setLatLngs(_points);
this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]);
_points.push(new LatLng(destination.lat, destination.lng));
this.#pathPolyline.setLatLngs(_points);
}
}
clearPath()
{
for (let WP in this._pathMarkers)
for (let WP in this.#pathMarkers)
{
map.getMap().removeLayer(this._pathMarkers[WP]);
map.getMap().removeLayer(this.#pathMarkers[WP]);
}
this._pathMarkers = [];
this._pathPolyline.setLatLngs([]);
this.#pathMarkers = [];
this.#pathPolyline.setLatLngs([]);
}
drawTargets()
{
for (let typeIndex in this.missionData.targets)
for (let typeIndex in this.missionData['targets'])
{
for (let index in this.missionData.targets[typeIndex])
for (let index in this.missionData['targets'][typeIndex])
{
var targetData = this.missionData.targets[typeIndex][index];
var targetData = this.missionData['targets'][typeIndex][index];
var target = unitsManager.getUnitByID(targetData.object["id_"])
if (target != undefined){
var startLatLng = new L.LatLng(this.latitude, this.longitude)
var endLatLng = new L.LatLng(target.latitude, target.longitude)
var startLatLng = new LatLng(this.latitude, this.longitude)
var endLatLng = new LatLng(target.latitude, target.longitude)
var color;
if (typeIndex === "radar")
@ -248,9 +297,9 @@ class Unit
{
color = "#FFFFFF";
}
var targetPolyline = new L.Polyline([startLatLng, endLatLng], {color: color, weight: 3, opacity: 1, smoothFactor: 1});
var targetPolyline = new Polyline([startLatLng, endLatLng], {color: color, weight: 3, opacity: 1, smoothFactor: 1});
targetPolyline.addTo(map.getMap());
this._targetsPolylines.push(targetPolyline)
this.#targetsPolylines.push(targetPolyline)
}
}
}
@ -258,9 +307,9 @@ class Unit
clearTargets()
{
for (let index in this._targetsPolylines)
for (let index in this.#targetsPolylines)
{
map.getMap().removeLayer(this._targetsPolylines[index])
map.getMap().removeLayer(this.#targetsPolylines[index])
}
}
@ -303,7 +352,7 @@ class Unit
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(this.unitName + " altitude change request: " + speedChange);
console.log(this.unitName + " altitude change request: " + altitudeChange);
}
};
@ -350,11 +399,11 @@ class Unit
}
}
class AirUnit extends Unit
export class AirUnit extends Unit
{
drawMarker()
{
if (this.flags.Human)
if (this.flags['Human'])
{
super.drawMarker(settingsPanel.getSettings().human);
}
@ -365,10 +414,11 @@ class AirUnit extends Unit
}
}
class Aircraft extends AirUnit
export class Aircraft extends AirUnit
{
constructor(ID, data)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
var marker = new L.Marker.UnitMarker.AirUnitMarker.AircraftMarker({
riseOnHover: true,
unitName: data.unitName,
@ -380,10 +430,11 @@ class Aircraft extends AirUnit
}
}
class Helicopter extends AirUnit
export class Helicopter extends AirUnit
{
constructor(ID, data)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
var marker = new L.Marker.UnitMarker.AirUnitMarker.HelicopterMarker({
riseOnHover: true,
unitName: data.unitName,
@ -395,10 +446,11 @@ class Helicopter extends AirUnit
}
}
class GroundUnit extends Unit
export class GroundUnit extends Unit
{
constructor(ID, data)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
var marker = new L.Marker.UnitMarker.GroundMarker({
riseOnHover: true,
unitName: data.unitName,
@ -415,10 +467,11 @@ class GroundUnit extends Unit
}
}
class NavyUnit extends Unit
export class NavyUnit extends Unit
{
constructor(ID, data)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
var marker = new L.Marker.UnitMarker.NavyMarker({
riseOnHover: true,
unitName: data.unitName,
@ -435,7 +488,7 @@ class NavyUnit extends Unit
}
}
class Weapon extends Unit
export class Weapon extends Unit
{
constructor(ID, data)
{
@ -449,16 +502,17 @@ class Weapon extends Unit
super.drawMarker(settingsPanel.getSettings().weapons);
}
onClick(e)
#onClick(e)
{
// Weapons can not be clicked
}
}
class Missile extends Weapon
export class Missile extends Weapon
{
constructor(ID, data)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
var marker = new L.Marker.UnitMarker.WeaponMarker.MissileMarker({
riseOnHover: true,
unitName: "",
@ -470,10 +524,11 @@ class Missile extends Weapon
}
}
class Bomb extends Weapon
export class Bomb extends Weapon
{
constructor(ID, data)
{
// @ts-ignore TODO: find a good way to extend markers in typescript
var marker = new L.Marker.UnitMarker.WeaponMarker.BombMarker({
riseOnHover: true,
unitName: "",

View File

@ -1,9 +1,17 @@
class UnitsManager
import { Unit } from 'Unit.js'
import { LatLng } from 'leaflet';
import { cloneUnit } from 'DCS/DCSCommands.js'
import { showMessage } from 'index.js';
export class UnitsManager
{
#units: { [ID: number]: Unit};
#copiedUnits: Unit[];
constructor()
{
this._units = {};
this._copiedUnits = [];
this.#units = {};
this.#copiedUnits = [];
}
addUnit(ID, data)
@ -12,13 +20,13 @@ class UnitsManager
var constructor = eval(data.category);
if (constructor != undefined)
{
this._units[ID] = new constructor(ID, data);
this.#units[ID] = new constructor(ID, data);
}
}
getUnitByID(ID)
{
return this._units[ID];
return this.#units[ID];
}
removeUnit(ID)
@ -28,9 +36,9 @@ class UnitsManager
deselectAllUnits()
{
for (let ID in this._units)
for (let ID in this.#units)
{
this._units[ID].setSelected(false);
this.#units[ID].setSelected(false);
}
}
@ -39,11 +47,11 @@ class UnitsManager
for (let ID in data["units"])
{
// Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet.
if (!(ID in this._units))
if (!(ID in this.#units))
{
this.addUnit(parseInt(ID), data["units"][ID]);
}
this._units[ID].update(data["units"][ID]);
this.#units[ID].update(data["units"][ID]);
}
}
@ -53,25 +61,23 @@ class UnitsManager
{
map.setState("MOVE_UNIT");
unitControlPanel.setEnabled(true);
actionPanel.setEnabled(true);
}
else
{
map.setState("IDLE");
unitControlPanel.setEnabled(false);
actionPanel.setEnabled(false);
}
}
selectFromBounds(bounds)
{
this.deselectAllUnits();
for (let ID in this._units)
for (let ID in this.#units)
{
var latlng = new L.LatLng(this._units[ID].latitude, this._units[ID].longitude);
var latlng = new LatLng(this.#units[ID].latitude, this.#units[ID].longitude);
if (bounds.contains(latlng))
{
this._units[ID].setSelected(true);
this.#units[ID].setSelected(true);
}
}
}
@ -79,11 +85,11 @@ class UnitsManager
getSelectedUnits()
{
var selectedUnits = [];
for (let ID in this._units)
for (let ID in this.#units)
{
if (this._units[ID].getSelected())
if (this.#units[ID].getSelected())
{
selectedUnits.push(this._units[ID]);
selectedUnits.push(this.#units[ID]);
}
}
return selectedUnits;
@ -94,7 +100,12 @@ class UnitsManager
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].addDestination(latlng);
var commandedUnit = selectedUnits[idx];
if (selectedUnits[idx].wingman)
{
commandedUnit = this.getLeader(selectedUnits[idx].ID);
}
commandedUnit.addDestination(latlng);
}
}
@ -103,7 +114,12 @@ class UnitsManager
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].clearDestinations();
var commandedUnit = selectedUnits[idx];
if (selectedUnits[idx].wingman)
{
commandedUnit = this.getLeader(selectedUnits[idx].ID);
}
commandedUnit.clearDestinations();
}
}
@ -144,14 +160,14 @@ class UnitsManager
copyUnits()
{
this._copiedUnits = this.getSelectedUnits();
this.#copiedUnits = this.getSelectedUnits();
}
pasteUnits()
{
for (let idx in this._copiedUnits)
for (let idx in this.#copiedUnits)
{
var unit = this._copiedUnits[idx];
var unit = this.#copiedUnits[idx];
cloneUnit(unit.ID);
}
}
@ -161,7 +177,13 @@ class UnitsManager
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].attackUnit(ID);
// If a unit is a wingman, send the command to its leader
var commandedUnit = selectedUnits[idx];
if (selectedUnits[idx].wingman)
{
commandedUnit = this.getLeader(selectedUnits[idx].ID);
}
commandedUnit.attackUnit(ID);
}
}
@ -183,10 +205,12 @@ class UnitsManager
}
else
{
/* TODO
if (selectedUnits[idx].category !== this.getUnitByID(ID).category)
{
showMessage("All units must be of the same category to create a formation.");
}
*/
if (selectedUnits[idx].ID != ID)
{
wingmenIDs.push(selectedUnits[idx].ID);
@ -203,28 +227,19 @@ class UnitsManager
}
}
getUnitsByFormationID(formationID)
getLeader(ID)
{
var formationUnits = [];
for (let ID in this._units)
{
if (this._units[ID].formationID == formationID)
{
formationUnits.push(this._units[ID]);
}
}
return formationUnits;
}
getLeaderByFormationID(formationID)
{
var formationUnits = this.getUnitsByFormationID(formationID);
for (let unit of formationUnits)
for (let idx in this.#units)
{
var unit = this.#units[idx];
if (unit.leader)
{
return unit;
if (unit.wingmen.includes(this.getUnitByID(ID)))
{
return unit;
}
}
}
showMessage("Error: no leader found for this unit")
}
}

View File

@ -1,5 +1,6 @@
var unitTypes = {};
var unitTypes: any = {};
/* NAVY */
unitTypes.navy = {};
unitTypes.navy.blue = [

18
www/src/globals.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
import {MissionData} from './Other/MissionData.js'
import {UnitsManager} from './Units/UnitsManager.js'
import {UnitControlPanel} from './Panels/UnitControlPanel.js'
import {UnitInfoPanel} from './Panels/UnitInfoPanel.js'
import {FormationControlPanel} from './Panels/FormationControlPanel.js'
import {SettingsPanel} from './Panels/SettingsPanel.js'
import {Map} from './Map/Map.js'
declare global {
var missionData: MissionData;
var settingsPanel: SettingsPanel;
var unitsManager: UnitsManager;
var unitInfoPanel: UnitInfoPanel;
var unitControlPanel: UnitControlPanel;
var formationControlPanel: FormationControlPanel;
var map: Map;
var RESTaddress: string;
}

View File

@ -1,12 +1,12 @@
var missionData;
var settingsPanel;
var unitsManager;
var unitInfoPanel;
var unitControlPanel;
var unitActionPanel;
var formationControlPanel;
var map;
var RESTaddress = "http://localhost:30000/restdemo";
import {MissionData} from './Other/MissionData.js'
import {UnitsManager} from './Units/UnitsManager.js'
import {UnitControlPanel} from './Panels/UnitControlPanel.js'
import {UnitInfoPanel} from './Panels/UnitInfoPanel.js'
import {FormationControlPanel} from './Panels/FormationControlPanel.js'
import {SettingsPanel} from './Panels/SettingsPanel.js'
import {Map} from './Map/Map.js'
RESTaddress = "http://localhost:30000/restdemo";
function setup()
{
@ -18,8 +18,7 @@ function setup()
unitControlPanel = new UnitControlPanel("unit-control-panel");
formationControlPanel = new FormationControlPanel("formation-control-panel");
settingsPanel = new SettingsPanel("settings-panel");
actionPanel = new ActionPanel("action-panel")
map = new Map();
map = new Map();
// Main update rate. 250ms is minimum time, equal to server update time.
setInterval(() => update(), 250);
@ -30,7 +29,6 @@ function resize()
var unitControlPanelHeight = document.getElementById("header").offsetHeight;
document.getElementById("map").style.height = `${window.innerHeight - unitControlPanelHeight - 10}px`;
document.getElementById("unit-control-panel").style.left = `${window.innerWidth / 2 - document.getElementById("unit-control-panel").offsetWidth / 2}px`
document.getElementById("action-panel").style.top = `${window.innerHeight / 2 - document.getElementById("action-panel").offsetHeight / 2}px`
document.getElementById("snackbar").style.left = `${window.innerWidth / 2 - document.getElementById("snackbar").offsetWidth / 2}px`
}
@ -59,33 +57,7 @@ function update()
window.onload = setup;
window.onresize = resize;
window.console = {
log: function(str){
if (str !== this.lastMessage)
{
var node = document.createElement("div");
node.classList.add("log-message");
node.appendChild(document.createTextNode("> " + str));
document.getElementById("log").appendChild(node);
this.lastMessage = str
}
},
error: function(str){
if (str !== this.lastMessage)
{
var node = document.createElement("div");
node.classList.add("error-message");
node.appendChild(document.createTextNode("> *** " + str + "***"));
document.getElementById("log").appendChild(node);
this.lastMessage = str
}
},
lastMessage: "none"
}
function showMessage(message)
export function showMessage(message)
{
// Get the snackbar DIV
var x = document.getElementById("snackbar");
@ -96,5 +68,4 @@ function showMessage(message)
// After 3 seconds, remove the show class from DIV
setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
}

View File

@ -1,11 +1,22 @@
{
"compilerOptions": {
"outDir": "./dist",
"allowJs": true,
"target": "es5"
"typeRoots": ["node_modules/@types"],
"outDir": "./js",
"target": "es2018",
"lib": [
"es2018",
"dom"
],
"types": [
"leaflet",
"geojson"
]
},
"include": [
"./js/*",
"./js/**/*"
"include": [
"src/**/*",
"src/*"
],
"exclude": [
"node_modules"
]
}
}