Merge branch 'main' into 329-add-basic-automatic-firefight

This commit is contained in:
Pax1601
2023-09-22 15:15:58 +02:00
211 changed files with 798 additions and 85185 deletions

View File

@@ -6,6 +6,7 @@ var fs = require('fs');
var atcRouter = require('./routes/api/atc');
var airbasesRouter = require('./routes/api/airbases');
var elevationRouter = require('./routes/api/elevation');
var indexRouter = require('./routes/index');
var uikitRouter = require('./routes/uikit');
var usersRouter = require('./routes/users');
@@ -23,6 +24,7 @@ app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/api/atc', atcRouter);
app.use('/api/airbases', airbasesRouter);
app.use('/api/elevation', elevationRouter);
app.use('/plugins', pluginsRouter)
app.use('/users', usersRouter);
app.use('/uikit', uikitRouter);

8387
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,7 @@
"scripts": {
"copy": "copy.bat",
"start": "npm run copy & concurrently --kill-others \"npm run watch\" \"nodemon ./bin/www\"",
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]",
"document": "typedoc --out ../docs src/index.ts src/contextmenus/*.ts src/controls/*.ts src/map/*.ts src/mission/*.ts src/other/*.ts src/panels/*.ts src/popups/*.ts src/server/*.ts src/unit/*.ts src/weapon/*.ts"
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
},
"dependencies": {
"cookie-parser": "~1.4.4",
@@ -17,7 +16,8 @@
"express": "~4.16.1",
"express-basic-auth": "^1.2.1",
"morgan": "~1.9.1",
"save": "^2.9.0"
"save": "^2.9.0",
"srtm-elevation": "^2.1.2"
},
"devDependencies": {
"@babel/preset-env": "^7.21.4",

View File

@@ -11,7 +11,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ControlTipsPlugin_instances, _ControlTipsPlugin_element, _ControlTipsPlugin_app, _ControlTipsPlugin_shortcutManager, _ControlTipsPlugin_cursorIsHoveringOverUnit, _ControlTipsPlugin_cursorIsHoveringOverAirbase, _ControlTipsPlugin_updateTips;
var _ControlTipsPlugin_instances, _ControlTipsPlugin_element, _ControlTipsPlugin_app, _ControlTipsPlugin_shortcutManager, _ControlTipsPlugin_cursorIsHoveringOverUnit, _ControlTipsPlugin_cursorIsHoveringOverAirbase, _ControlTipsPlugin_mouseoverElement, _ControlTipsPlugin_updateTips;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ControlTipsPlugin = void 0;
const SHOW_CONTROL_TIPS = "Show control tips";
@@ -23,6 +23,7 @@ class ControlTipsPlugin {
_ControlTipsPlugin_shortcutManager.set(this, void 0);
_ControlTipsPlugin_cursorIsHoveringOverUnit.set(this, false);
_ControlTipsPlugin_cursorIsHoveringOverAirbase.set(this, false);
_ControlTipsPlugin_mouseoverElement.set(this, void 0);
__classPrivateFieldSet(this, _ControlTipsPlugin_element, document.createElement("div"), "f");
__classPrivateFieldGet(this, _ControlTipsPlugin_element, "f").id = "control-tips-panel";
document.body.appendChild(__classPrivateFieldGet(this, _ControlTipsPlugin_element, "f"));
@@ -64,6 +65,15 @@ class ControlTipsPlugin {
document.addEventListener("mapVisibilityOptionsChanged", () => {
this.toggle(!__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS]);
});
document.addEventListener("mouseover", (ev) => {
if (ev.target instanceof HTMLElement) {
__classPrivateFieldSet(this, _ControlTipsPlugin_mouseoverElement, ev.target, "f");
}
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
});
document.addEventListener("mouseup", (ev) => {
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
});
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getMap().addVisibilityOption(SHOW_CONTROL_TIPS, true);
return true;
@@ -76,7 +86,7 @@ class ControlTipsPlugin {
}
}
exports.ControlTipsPlugin = ControlTipsPlugin;
_ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap(), _ControlTipsPlugin_shortcutManager = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverUnit = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTipsPlugin_instances = new WeakSet(), _ControlTipsPlugin_updateTips = function _ControlTipsPlugin_updateTips() {
_ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap(), _ControlTipsPlugin_shortcutManager = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverUnit = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTipsPlugin_mouseoverElement = new WeakMap(), _ControlTipsPlugin_instances = new WeakSet(), _ControlTipsPlugin_updateTips = function _ControlTipsPlugin_updateTips() {
const combos = [
{
"keys": [],
@@ -128,21 +138,27 @@ _ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": "CTRL+Mouse2",
"action": "Add waypoint",
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"unitsMustBeControlled": true
},
{
"key": `Mouse2 (hold)`,
"action": `Point operations`,
"action": `Interact (ground)`,
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"showIfHoveringOverUnit": false,
"unitsMustBeControlled": true
},
{
"key": `Shift`,
"action": "<em> in formation...</em>",
"showIfUnitSelected": true,
"minSelectedUnits": 2
},
{
"key": "CTRL",
"action": "<em> ... more</em>",
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"unitsMustBeControlled": true
},
{
"key": "CTRL",
"action": " Pin tool",
@@ -159,10 +175,14 @@ _ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap
"unitsMustBeControlled": true
},
{
"key": `Delete`,
"action": `Delete unit`,
"showIfHoveringOverAirbase": false,
"showIfUnitSelected": true
"key": `Mouse1`,
"action": "Toggle Blue/Red",
"mouseoverSelector": "#coalition-switch .ol-switch-fill"
},
{
"key": `Mouse2`,
"action": "Set Neutral",
"mouseoverSelector": "#coalition-switch .ol-switch-fill"
}
]
},
@@ -187,15 +207,35 @@ _ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap
"key": `Mouse2`,
"action": `Add waypoint`,
"showIfHoveringOverAirbase": false,
"showIfHoveringOverUnit": false,
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": `Mouse2`,
"action": `Airbase menu`,
"action": `Interact (airbase)`,
"showIfHoveringOverAirbase": true,
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": `Mouse2`,
"action": `Interact (unit)`,
"showIfHoveringOverAirbase": false,
"showIfHoveringOverUnit": true,
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": `Shift`,
"action": "<em> in formation...</em>",
"showIfUnitSelected": true,
"minSelectedUnits": 2
},
{
"key": `[Num 1-9]`,
"action": "Set hotgroup",
"showIfUnitSelected": true
}
]
},
@@ -203,8 +243,35 @@ _ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap
"keys": ["ShiftLeft"],
"tips": [
{
"key": `mouse1+drag`,
"action": "Box select"
"key": `Mouse1+drag`,
"action": "Box select",
"showIfUnitSelected": false
},
{
"key": `Mouse2`,
"action": "Set first formation waypoint",
"showIfUnitSelected": true,
"minSelectedUnits": 2
},
{
"key": "CTRL",
"action": "<em> ... more</em>",
"minSelectedUnits": 2,
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"unitsMustBeControlled": true
}
]
},
{
"keys": ["ControlLeft", "ShiftLeft"],
"tips": [
{
"key": `Mouse2`,
"action": "Add formation waypoint",
"showIfUnitSelected": true,
"minSelectedUnits": 2,
"unitsMustBeControlled": true
}
]
}
@@ -213,34 +280,57 @@ _ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap
const element = this.getElement();
element.innerHTML = "";
let numSelectedUnits = 0;
let numSelectedControlledUnits = 0;
let unitSelectionContainsControlled = false;
if (__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager()) {
let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager().getSelectedUnits());
numSelectedUnits = selectedUnits.length;
unitSelectionContainsControlled = selectedUnits.some((unit) => unit.getControlled());
numSelectedControlledUnits = selectedUnits.filter((unit) => unit.getControlled()).length;
unitSelectionContainsControlled = numSelectedControlledUnits > 0;
}
currentCombo.tips.forEach((tip) => {
const tipsIncludesActiveMouseover = (currentCombo.tips.some((tip) => {
if (!tip.mouseoverSelector) {
return false;
}
if (__classPrivateFieldGet(this, _ControlTipsPlugin_mouseoverElement, "f") instanceof HTMLElement === false) {
return false;
}
if (!__classPrivateFieldGet(this, _ControlTipsPlugin_mouseoverElement, "f").matches(tip.mouseoverSelector)) {
return false;
}
return true;
}));
currentCombo.tips.filter((tip) => {
if (numSelectedUnits > 0) {
if (tip.showIfUnitSelected === false) {
return;
return false;
}
if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) {
return;
return false;
}
if (typeof tip.minSelectedUnits === "number" && numSelectedControlledUnits < tip.minSelectedUnits) {
return false;
}
}
if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) {
return;
return false;
}
if (typeof tip.showIfHoveringOverAirbase === "boolean") {
if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, "f")) {
return;
return false;
}
}
if (typeof tip.showIfHoveringOverUnit === "boolean") {
if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, "f")) {
return;
return false;
}
}
if (tipsIncludesActiveMouseover && typeof tip.mouseoverSelector !== "string" && !__classPrivateFieldGet(this, _ControlTipsPlugin_mouseoverElement, "f").matches(tip.mouseoverSelector)) {
return false;
}
if (!tipsIncludesActiveMouseover && typeof tip.mouseoverSelector === "string") {
return false;
}
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`;
});
};
@@ -248,9 +338,9 @@ _ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap
},{}],2:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const controltips_1 = require("./controltips");
const controltipsplugin_1 = require("./controltipsplugin");
globalThis.getOlympusPlugin = () => {
return new controltips_1.ControlTipsPlugin();
return new controltipsplugin_1.ControlTipsPlugin();
};
},{"./controltips":1}]},{},[2]);
},{"./controltipsplugin":1}]},{},[2]);

View File

@@ -6,6 +6,7 @@ export class ControlTipsPlugin implements OlympusPlugin {
#shortcutManager: any;
#cursorIsHoveringOverUnit: boolean = false;
#cursorIsHoveringOverAirbase: boolean = false;
#mouseoverElement!: HTMLElement;
constructor() {
this.#element = document.createElement("div");
@@ -55,13 +56,24 @@ export class ControlTipsPlugin implements OlympusPlugin {
});
document.addEventListener("unitSelection", (ev: CustomEvent) => {
this.#updateTips()
this.#updateTips();
});
document.addEventListener("mapVisibilityOptionsChanged", () => {
this.toggle( !this.#app.getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS] );
});
document.addEventListener( "mouseover", ( ev: MouseEvent ) => {
if ( ev.target instanceof HTMLElement ) {
this.#mouseoverElement = <HTMLElement>ev.target;
}
this.#updateTips();
});
document.addEventListener( "mouseup", ( ev: MouseEvent ) => {
this.#updateTips();
});
this.#updateTips();
this.#app.getMap().addVisibilityOption(SHOW_CONTROL_TIPS, true);
@@ -129,21 +141,27 @@ export class ControlTipsPlugin implements OlympusPlugin {
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": "CTRL+Mouse2",
"action": "Add waypoint",
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"unitsMustBeControlled": true
},
{
"key": `Mouse2 (hold)`,
"action": `Point operations`,
"action": `Interact (ground)`,
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"showIfHoveringOverUnit": false,
"unitsMustBeControlled": true
},
{
"key": `Shift`,
"action": "<em> in formation...</em>",
"showIfUnitSelected": true,
"minSelectedUnits": 2
},
{
"key": "CTRL",
"action": "<em> ... more</em>",
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"unitsMustBeControlled": true
},
{
"key": "CTRL",
"action": " Pin tool",
@@ -160,10 +178,14 @@ export class ControlTipsPlugin implements OlympusPlugin {
"unitsMustBeControlled": true
},
{
"key": `Delete`,
"action": `Delete unit`,
"showIfHoveringOverAirbase": false,
"showIfUnitSelected": true
"key": `Mouse1`,
"action": "Toggle Blue/Red",
"mouseoverSelector": "#coalition-switch .ol-switch-fill"
},
{
"key": `Mouse2`,
"action": "Set Neutral",
"mouseoverSelector": "#coalition-switch .ol-switch-fill"
}
]
},
@@ -188,15 +210,35 @@ export class ControlTipsPlugin implements OlympusPlugin {
"key": `Mouse2`,
"action": `Add waypoint`,
"showIfHoveringOverAirbase": false,
"showIfHoveringOverUnit": false,
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": `Mouse2`,
"action": `Airbase menu`,
"action": `Interact (airbase)`,
"showIfHoveringOverAirbase": true,
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": `Mouse2`,
"action": `Interact (unit)`,
"showIfHoveringOverAirbase": false,
"showIfHoveringOverUnit": true,
"showIfUnitSelected": true,
"unitsMustBeControlled": true
},
{
"key": `Shift`,
"action": "<em> in formation...</em>",
"showIfUnitSelected": true,
"minSelectedUnits": 2
},
{
"key": `[Num 1-9]`,
"action": "Set hotgroup",
"showIfUnitSelected": true
}
]
},
@@ -204,8 +246,35 @@ export class ControlTipsPlugin implements OlympusPlugin {
"keys": ["ShiftLeft"],
"tips": [
{
"key": `mouse1+drag`,
"action": "Box select"
"key": `Mouse1+drag`,
"action": "Box select",
"showIfUnitSelected": false
},
{
"key": `Mouse2`,
"action": "Set first formation waypoint",
"showIfUnitSelected": true,
"minSelectedUnits": 2
},
{
"key": "CTRL",
"action": "<em> ... more</em>",
"minSelectedUnits": 2,
"showIfUnitSelected": true,
"showIfHoveringOverAirbase": false,
"unitsMustBeControlled": true
}
]
},
{
"keys": ["ControlLeft", "ShiftLeft"],
"tips": [
{
"key": `Mouse2`,
"action": "Add formation waypoint",
"showIfUnitSelected": true,
"minSelectedUnits": 2,
"unitsMustBeControlled": true
}
]
}
@@ -218,42 +287,73 @@ export class ControlTipsPlugin implements OlympusPlugin {
element.innerHTML = "";
let numSelectedUnits = 0;
let numSelectedControlledUnits = 0;
let unitSelectionContainsControlled = false;
if (this.#app.getUnitsManager()) {
let selectedUnits = Object.values(this.#app.getUnitsManager().getSelectedUnits());
numSelectedUnits = selectedUnits.length;
unitSelectionContainsControlled = selectedUnits.some((unit: any) => unit.getControlled());
numSelectedUnits = selectedUnits.length;
numSelectedControlledUnits = selectedUnits.filter((unit: any) => unit.getControlled()).length;
unitSelectionContainsControlled = numSelectedControlledUnits > 0;
}
currentCombo.tips.forEach((tip: any) => {
const tipsIncludesActiveMouseover = ( currentCombo.tips.some( ( tip:any ) => {
if ( !tip.mouseoverSelector ) {
return false;
}
if ( this.#mouseoverElement instanceof HTMLElement === false ) {
return false;
}
if ( !this.#mouseoverElement.matches( tip.mouseoverSelector ) ) {
return false;
}
return true;
}));
currentCombo.tips.filter((tip: any) => {
if (numSelectedUnits > 0) {
if (tip.showIfUnitSelected === false) {
return;
return false;
}
if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) {
return;
return false;
}
if ( typeof tip.minSelectedUnits === "number" && numSelectedControlledUnits < tip.minSelectedUnits ) {
return false;
}
}
if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) {
return;
return false;
}
if (typeof tip.showIfHoveringOverAirbase === "boolean") {
if (tip.showIfHoveringOverAirbase !== this.#cursorIsHoveringOverAirbase) {
return;
return false;
}
}
if (typeof tip.showIfHoveringOverUnit === "boolean") {
if (tip.showIfHoveringOverUnit !== this.#cursorIsHoveringOverUnit) {
return;
return false;
}
}
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`
if ( tipsIncludesActiveMouseover && typeof tip.mouseoverSelector !== "string" && !this.#mouseoverElement.matches( tip.mouseoverSelector ) ) {
return false;
}
if ( !tipsIncludesActiveMouseover && typeof tip.mouseoverSelector === "string" ) {
return false;
}
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`;
});
}
}

View File

@@ -60,6 +60,11 @@
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
@@ -646,7 +651,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {

View File

@@ -106,4 +106,14 @@
text-overflow: ellipsis;
white-space: nowrap;
color: var(--background-offwhite);
}
.elevation::after {
content: attr(data-value);
font-weight: bold;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--background-offwhite);
}

View File

@@ -0,0 +1,28 @@
const express = require('express');
var fs = require('fs');
const router = express.Router();
const TileSet = require('srtm-elevation').TileSet;
const SRTMElevationDownloader = require('srtm-elevation').SRTMElevationDownloader;
let rawdata = fs.readFileSync('../olympus.json');
let config = JSON.parse(rawdata);
var tileset = null;
if (config["client"] === undefined || config["client"]["elevationProvider"] === undefined)
tileset = new TileSet('./hgt');
else
tileset = new TileSet('./hgt', {downloader: new SRTMElevationDownloader('./hgt', config["client"]["elevationProvider"])});
router.get( "/:lat/:lng", ( req, res ) => {
tileset.getElevation([req.params.lat, req.params.lng], function(err, elevation) {
if (err) {
console.log('getElevation failed: ' + err.message);
res.send("n/a");
} else {
res.send(String(elevation));
}
});
});
module.exports = router;

View File

@@ -16,7 +16,6 @@ function listDirectories(source) {
router.get('/list', function (req, res) {
var directories = listDirectories(pluginsDirectory);
console.log(directories)
res.send(directories.filter(directory => fs.existsSync(path.join(pluginsDirectory, directory))));
});

View File

@@ -101,7 +101,7 @@ export const mapBounds = {
// TODO "Falklands"
}
export const layers = {
export const mapLayers = {
"ArcGIS Satellite": {
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
maxZoom: 20,

View File

@@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import { SVGInjector } from '@tanem/svg-injector'
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTooltips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants";
import { mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTooltips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants";
import { TargetMarker } from "./markers/targetmarker";
import { CoalitionArea } from "./coalitionarea/coalitionarea";
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";

View File

@@ -1,6 +1,6 @@
import { Icon, LatLng, Marker, Polyline } from "leaflet";
import { getApp } from "..";
import { distance, bearing, zeroAppend, mToNm, nmToFt } from "../other/utils";
import { distance, bearing, zeroAppend, mToNm, nmToFt, mToFt } from "../other/utils";
import { Unit } from "../unit/unit";
import { Panel } from "./panel";
import formatcoords from "formatcoords";
@@ -11,6 +11,7 @@ export class MouseInfoPanel extends Panel {
#measureIcon: Icon;
#measureLine: Polyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
#measureBox: HTMLElement;
#elevationRequest: XMLHttpRequest | null = null;
constructor(ID: string) {
super( ID );
@@ -53,6 +54,30 @@ export class MouseInfoPanel extends Panel {
var coordString = coords.format('XDDMMss', {decimalPlaces: 4});
this.#drawCoordinates("ref-mouse-position-latitude", "mouse-position-latitude", coordString.split(" ")[0]);
this.#drawCoordinates("ref-mouse-position-longitude", "mouse-position-longitude", coordString.split(" ")[1]);
/* Get the ground elevation from the server endpoint */
if (this.#elevationRequest == null) {
this.#elevationRequest = new XMLHttpRequest();
this.#elevationRequest.open('GET', `api/elevation/${mousePosition.lat}/${mousePosition.lng}`, true);
this.#elevationRequest.timeout = 500; // ms
this.#elevationRequest.responseType = 'json';
this.#elevationRequest.onload = () => {
var status = this.#elevationRequest?.status;
if (status === 200) {
const el = this.getElement().querySelector(`#mouse-position-elevation`) as HTMLElement;
try {
el.dataset.value = `${Math.floor(mToFt(parseFloat(this.#elevationRequest?.response)))} ft`;
} catch {
el.dataset.value = `N/A`;
}
}
this.#elevationRequest = null;
};
this.#elevationRequest.ontimeout = () => {this.#elevationRequest = null;}
this.#elevationRequest.onerror = () => {this.#elevationRequest = null;}
this.#elevationRequest.onabort = () => {this.#elevationRequest = null;}
this.#elevationRequest.send();
}
}
#onMapClick(e: any) {
@@ -115,7 +140,6 @@ export class MouseInfoPanel extends Panel {
}
#onMouseMove(e: any) {
this.#update();
this.#drawMeasureLine();
}

View File

@@ -81,14 +81,14 @@ export class Unit extends CustomMarker {
#selected: boolean = false;
#hidden: boolean = false;
#highlighted: boolean = false;
#preventClick: boolean = false;
#waitingForDoubleClick: boolean = false;
#pathMarkers: Marker[] = [];
#pathPolyline: Polyline;
#contactsPolylines: Polyline[];
#miniMapMarker: CircleMarker | null = null;
#targetPositionMarker: TargetMarker;
#targetPositionPolyline: Polyline;
#timer: number = 0;
#doubleClickTimer: number = 0;
#hotgroup: number | null = null;
#detectionMethods: number[] = [];
@@ -734,32 +734,49 @@ export class Unit extends CustomMarker {
/***********************************************/
#onClick(e: any) {
if (!this.#preventClick) {
if (getApp().getMap().getState() === IDLE || getApp().getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
if (!e.originalEvent.ctrlKey)
getApp().getUnitsManager().deselectAllUnits();
this.setSelected(!this.getSelected());
const detail = { "detail": { "unit": this } };
if (this.getSelected())
document.dispatchEvent(new CustomEvent("unitSelected", detail));
else
document.dispatchEvent(new CustomEvent("unitDeselection", { "detail": this }));
}
// Exit if we were waiting for a doubleclick
if (this.#waitingForDoubleClick) {
return;
}
// We'll wait for a doubleclick
this.#waitingForDoubleClick = true;
this.#timer = window.setTimeout(() => { this.#preventClick = false; }, 200);
this.#doubleClickTimer = window.setTimeout(() => {
// Still waiting so no doubleclick; do the click action
if (this.#waitingForDoubleClick) {
if (getApp().getMap().getState() === IDLE || getApp().getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
if (!e.originalEvent.ctrlKey)
getApp().getUnitsManager().deselectAllUnits();
this.setSelected(!this.getSelected());
const detail = { "detail": { "unit": this } };
if (this.getSelected())
document.dispatchEvent(new CustomEvent("unitSelected", detail));
else
document.dispatchEvent(new CustomEvent("unitDeselection", { "detail": this }));
}
}
// No longer waiting for a doubleclick
this.#waitingForDoubleClick = false;
}, 200);
}
#onDoubleClick(e: any) {
// Let single clicks work again
this.#waitingForDoubleClick = false;
clearTimeout(this.#doubleClickTimer);
// Select all matching units in the viewport
const unitsManager = getApp().getUnitsManager();
Object.values(unitsManager.getUnits()).forEach((unit: Unit) => {
if (unit.getAlive() === true && unit.getName() === this.getName() && unit.isInViewport())
unitsManager.selectUnit(unit.ID, false);
});
clearTimeout(this.#timer);
this.#preventClick = true;
}
#onContextMenu(e: any) {

View File

@@ -36,9 +36,7 @@
"leaflet",
"geojson",
"node",
"formatcoords",
"olympus",
"dom"
"formatcoords"
], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */

View File

@@ -25,9 +25,11 @@
<div id="coordinates-tool">
<dl class="ol-data-grid">
<dt id="ref-mouse-position-latitude" data-label="?"></dt>
<dd id="mouse-position-latitude" class="coordinates" data-dd="---" data-mm="---" data-ss="--" data-sss="--"></dd>
<dd id="mouse-position-latitude" class="coordinates"></dd>
<dt id="ref-mouse-position-longitude" data-label="?"></dt>
<dd id="mouse-position-longitude" class="coordinates" data-dd="---" data-mm="---" data-ss="--" data-sss="--"></dd>
<dd id="mouse-position-longitude" class="coordinates"></dd>
<dt id="ref-mouse-position-elevation" data-label="H"></dt>
<dd id="mouse-position-elevation" class="elevation" data-value="---"></dd>
</dl>
</div>