Nullam iaculis nisi sed mi tincidunt pretium blandit tempus urna. Vestibulum non ex vitae massa tristique auctor. Praesent orci justo, porttitor pellentesque convallis non, commodo at augue.
+
+
+
+
In a panel
+
+
Donec nibh est, fringilla sed pharetra eu, varius vel sem. Aliquam ac libero leo. Sed consectetur enim aliquam dui pellentesque luctus. Pellentesque vel iaculis quam.
+
+
+
+
+
+
+
+
+
+
.ol-panel
+
+
+
+
+
+
Plain panel
+
+
+
+
+
+
+ Disconnected
+
+
+
+
+
+
+
+
+
+
+
Panel list
+
+
+
+
+
+
Basic list
+
+
+
+
List item 1
+
List item 2
+
List item 3
+
+
+
+
+
+
+
+
List with .highlight-primary
+
+
+
+
List item with highlight-primary
+
List item with highlight-bluefor
+
List item with highlight-redfor
+
List item with highlight-neutral
+
+
+
+
+
+
+
+
Sortable list
+
+
+
+
+
+
List item 1
+
+
+
+
List item 2
+
+
+
+
List item 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
.ol-panel > .ol-panel-info
+
+
+
+
+
+
+
+
+ Info panel number 1
+
+
+ Info panel number 2
+
+
+ Info panel number 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/FeatureSwitches.ts b/client/src/FeatureSwitches.ts
new file mode 100644
index 00000000..6cf723d0
--- /dev/null
+++ b/client/src/FeatureSwitches.ts
@@ -0,0 +1,77 @@
+export interface FeatureSwitchInterface {
+ "enabled": boolean,
+ "label": string,
+ "name": string,
+ "options"?: object,
+ "removeArtifactsIfDisabled"?: boolean
+}
+
+
+class FeatureSwitch {
+ enabled;
+ label;
+ name;
+ removeArtifactsIfDisabled = true;
+
+ constructor( config:FeatureSwitchInterface ) {
+
+ this.enabled = config.enabled;
+ this.label = config.label;
+ this.name = config.name;
+
+ }
+
+
+ isEnabled() {
+ return this.enabled;
+ }
+
+}
+
+export class FeatureSwitches {
+
+ #featureSwitches:FeatureSwitch[] = [
+
+ new FeatureSwitch({
+ "enabled": false,
+ "label": "AIC",
+ "name": "aic"
+ }),
+
+ new FeatureSwitch({
+ "enabled": false,
+ "label": "ATC",
+ "name": "atc",
+ "options": {
+ "key": "value"
+ }
+ })
+
+ ];
+
+
+ constructor() {
+
+ this.#removeArtifacts();
+
+ }
+
+
+ getSwitch( switchName:string ) {
+
+ return this.#featureSwitches.find( featureSwitch => featureSwitch.name === switchName );
+
+ }
+
+
+ #removeArtifacts() {
+
+ for ( const featureSwitch of this.#featureSwitches ) {
+ if ( !featureSwitch.isEnabled() && featureSwitch.removeArtifactsIfDisabled !== false ) {
+ document.querySelectorAll( "[data-feature-switch='" + featureSwitch.name + "']" ).forEach( el => el.remove() );
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/ToggleableFeature.ts b/client/src/ToggleableFeature.ts
new file mode 100644
index 00000000..5873fb6e
--- /dev/null
+++ b/client/src/ToggleableFeature.ts
@@ -0,0 +1,35 @@
+export abstract class ToggleableFeature {
+
+ #status:boolean = false;
+
+
+ constructor( defaultStatus:boolean ) {
+
+ this.#status = defaultStatus;
+
+ this.onStatusUpdate();
+
+ }
+
+
+ getStatus() : boolean {
+ return this.#status;
+ }
+
+
+ protected onStatusUpdate() {}
+
+
+ toggleStatus( force?:boolean ) : void {
+
+ if ( force ) {
+ this.#status = force;
+ } else {
+ this.#status = !this.#status;
+ }
+
+ this.onStatusUpdate();
+
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/aic/aic.ts b/client/src/aic/aic.ts
index a9d65322..a28aaa9d 100644
--- a/client/src/aic/aic.ts
+++ b/client/src/aic/aic.ts
@@ -1,12 +1,11 @@
+import { ToggleableFeature } from "../ToggleableFeature";
import { AICFormation_Azimuth } from "./AICFormation/Azimuth";
import { AICFormation_Range } from "./AICFormation/Range";
import { AICFormation_Single } from "./AICFormation/Single";
import { AICFormationDescriptorSection } from "./AICFormationDescriptorSection";
-export class AIC {
-
- #status:boolean = true;
+export class AIC extends ToggleableFeature {
#formations = [
@@ -18,8 +17,10 @@ export class AIC {
constructor() {
+
+ super( false );
- this.#onStatusUpdate();
+ this.onStatusUpdate();
// This feels kind of dirty
let $aicFormationList = document.getElementById( "aic-formation-list" );
@@ -80,30 +81,10 @@ export class AIC {
}
- getStatus() {
- return this.#status;
- }
-
-
- #onStatusUpdate() {
-
- const status:boolean = this.getStatus();
+ onStatusUpdate() {
// Update the DOM
- document.body.classList.toggle( "aic-enabled", status );
-
- }
-
-
- toggleStatus(force?:boolean) {
-
- if ( force ) {
- this.#status = force;
- } else {
- this.#status = !this.#status;
- }
-
- this.#onStatusUpdate();
+ document.body.classList.toggle( "aic-enabled", this.getStatus() );
}
diff --git a/client/src/atc/ATC.ts b/client/src/atc/ATC.ts
new file mode 100644
index 00000000..c409b02e
--- /dev/null
+++ b/client/src/atc/ATC.ts
@@ -0,0 +1,87 @@
+import { ToggleableFeature } from "../ToggleableFeature";
+import Sortable from 'sortablejs';
+import { ATCFLightList } from "./FlightList";
+
+export class ATC extends ToggleableFeature {
+
+ constructor() {
+
+ super( true );
+
+ //this.#generateFlightList();
+
+ let $list = document.getElementById( "atc-strip-board-arrivals" );
+
+ if ( $list instanceof HTMLElement ) {
+ Sortable.create( $list, {
+ "handle": ".handle"
+ });
+ }
+
+ }
+
+
+ #generateFlightList() {
+
+ const flightList = new ATCFLightList();
+ const flights:any = flightList.getFlights( true );
+
+ const $tbody = document.getElementById( "atc-flight-list-table-body" );
+
+ if ( $tbody instanceof HTMLElement ) {
+
+ if ( flights.length > 0 ) {
+
+ let flight:any = {};
+
+ let $button, i;
+
+ for ( [ i, flight ] of flights.entries() ) {
+
+ const $row = document.createElement( "tr" );
+ $row.dataset.status = flight.status
+
+ let $td = document.createElement( "td" );
+ $td.innerText = flight.name;
+ $row.appendChild( $td );
+
+ $td = document.createElement( "td" );
+ $td.innerText = flight.takeOffTime;
+ $row.appendChild( $td );
+
+ $td = document.createElement( "td" );
+ $td.innerText = "00:0" + ( 5 + i );
+ $row.appendChild( $td );
+
+ $td = document.createElement( "td" );
+ $td.innerText = flight.status;
+ $row.appendChild( $td );
+
+
+ $td = document.createElement( "td" );
+ $button = document.createElement( "button" );
+ $button.innerText = "...";
+
+ $td.appendChild( $button );
+
+ $row.appendChild( $td );
+
+
+ $tbody.appendChild( $row );
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ protected onStatusUpdate(): void {
+
+ document.body.classList.toggle( "atc-enabled", this.getStatus() );
+
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/atc/ATCAPI.ts b/client/src/atc/ATCAPI.ts
deleted file mode 100644
index 22aaa3eb..00000000
--- a/client/src/atc/ATCAPI.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-
-export interface ATCAPIInterface {
- get: CallableFunction
-}
-
-export abstract class ATCAPI {
-
-
-
- constructor() {
-
- }
-
-}
\ No newline at end of file
diff --git a/client/src/atc/ATCMockAPI.ts b/client/src/atc/ATCMockAPI.ts
new file mode 100644
index 00000000..9720e2d0
--- /dev/null
+++ b/client/src/atc/ATCMockAPI.ts
@@ -0,0 +1,7 @@
+export abstract class ATCMockAPI {
+
+ constructor() {}
+
+ generateMockData() {}
+
+}
\ No newline at end of file
diff --git a/client/src/atc/ATCMockAPI/Flights.ts b/client/src/atc/ATCMockAPI/Flights.ts
new file mode 100644
index 00000000..b710b016
--- /dev/null
+++ b/client/src/atc/ATCMockAPI/Flights.ts
@@ -0,0 +1,40 @@
+import { ATCMockAPI } from "../ATCMockAPI";
+
+export class ATCMockAPI_Flights extends ATCMockAPI {
+
+
+ generateMockData() {
+
+ let data = [];
+ const statuses = [ "unknown", "checkedIn", "readyToTaxi" ]
+
+ for ( const [ i, flightName ] of [ "Shark", "Whale", "Dolphin" ].entries() ) {
+
+ data.push({
+ "name": flightName,
+ "status": statuses[ i ],
+ "takeOffTime": "18:0" + i
+ });
+
+ }
+
+ localStorage.setItem( "flightList", JSON.stringify( data ) );
+
+ }
+
+
+ get( generateMockDataIfEmpty?:boolean ) : object {
+
+ generateMockDataIfEmpty = generateMockDataIfEmpty || false;
+
+ let data = localStorage.getItem( "flightList" ) || "[]";
+
+ if ( data === "[]" && generateMockDataIfEmpty ) {
+ this.generateMockData();
+ }
+
+ return JSON.parse( data );
+
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/atc/FlightList.ts b/client/src/atc/FlightList.ts
new file mode 100644
index 00000000..a24a55e2
--- /dev/null
+++ b/client/src/atc/FlightList.ts
@@ -0,0 +1,18 @@
+import { ATCMockAPI_Flights } from "./ATCMockAPI/Flights";
+
+export class ATCFLightList {
+
+
+ constructor() {
+
+
+
+ }
+
+
+ getFlights( generateMockDataIfEmpty?:boolean ) {
+ let api = new ATCMockAPI_Flights();
+ return api.get( generateMockDataIfEmpty );
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/index.ts b/client/src/index.ts
index a04de9e5..de4c3ae4 100644
--- a/client/src/index.ts
+++ b/client/src/index.ts
@@ -14,6 +14,8 @@ import { Slider } from "./controls/slider";
import { AIC } from "./aic/AIC";
import { VisibilityControlPanel } from "./panels/visibilitycontrolpanel";
+import { ATC } from "./atc/ATC";
+import { FeatureSwitches } from "./FeatureSwitches";
/* TODO: should this be a class? */
var map: Map;
@@ -41,13 +43,22 @@ var aic: AIC;
var aicToggleButton: Button;
var aicHelpButton: Button;
+
+var atc: ATC;
+var atcToggleButton: Button;
+
var altitudeSlider: Slider;
var airspeedSlider: Slider;
var connected: boolean;
var activeCoalition: string;
+var featureSwitches;
+
function setup() {
+
+ featureSwitches = new FeatureSwitches();
+
/* Initialize */
map = new Map('map-container');
unitsManager = new UnitsManager();
@@ -63,9 +74,6 @@ function setup() {
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
visibilityControlPanel = new VisibilityControlPanel("visibility-control-panel");
- scenarioDropdown = new Dropdown("scenario-dropdown", ["Caucasus", "Syria", "Marianas", "Nevada", "South Atlantic", "The channel"], () => { });
- mapSourceDropdown = new Dropdown("map-source-dropdown", map.getLayers(), (option: string) => map.setLayer(option));
-
missionData = new MissionData();
/* Unit control buttons */
@@ -79,15 +87,20 @@ function setup() {
airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => getUnitsManager().selectedUnitsSetSpeed(value / 1.94384));
/* AIC */
- aic = new AIC();
- aicToggleButton = new Button( "toggle-aic-button", ["images/buttons/radar.svg"], () => {
- aic.toggleStatus();
- });
+ let aicFeatureSwitch = featureSwitches.getSwitch( "aic" );
- aicHelpButton = new Button( "aic-help-button", [ "images/buttons/question-mark.svg" ], () => {
- aic.toggleHelp();
- });
+ if ( aicFeatureSwitch?.isEnabled() ) {
+ aic = new AIC();
+
+ aicToggleButton = new Button( "toggle-aic-button", ["images/buttons/radar.svg"], () => {
+ aic.toggleStatus();
+ });
+
+ aicHelpButton = new Button( "aic-help-button", [ "images/buttons/question-mark.svg" ], () => {
+ aic.toggleHelp();
+ });
+ }
/* Generic clicks */
@@ -104,6 +117,22 @@ function setup() {
});
+
+ /*** ATC ***/
+
+ let atcFeatureSwitch = featureSwitches.getSwitch( "atc" );
+
+ if ( atcFeatureSwitch?.isEnabled() ) {
+
+ atc = new ATC();
+
+ atcToggleButton = new Button( "atc-toggle-button", [ "images/buttons/atc.svg" ], () => {
+ atc.toggleStatus();
+ } );
+
+ }
+
+
/* Default values */
activeCoalition = "blue";
connected = false;
diff --git a/client/views/aiccontrolpanel.ejs b/client/views/aiccontrolpanel.ejs
index 5a52381d..a1486db4 100644
--- a/client/views/aiccontrolpanel.ejs
+++ b/client/views/aiccontrolpanel.ejs
@@ -1,9 +1,9 @@
-