// ==UserScript==
// @name * Belegungsanzeiger
// @namespace bos-ernie.leitstellenspiel.de
// @version 1.6.0
// @license BSD-3-Clause
// @author BOS-Ernie
// @description Zeigt die Belegung von Betten, Zellen und Schulen der eigenen und Verbandsgebäuden an.
// @match https://www.leitstellenspiel.de/
// @match https://polizei.leitstellenspiel.de/
// @icon https://www.google.com/s2/favicons?sz=64&domain=leitstellenspiel.de
// @run-at document-idle
// @grant none
// @resource https://forum.leitstellenspiel.de/index.php?thread/23822-script-belegungsanzeiger-by-bos-ernie/
// ==/UserScript==
/**
* - Belegungsmarker integrieren
* - Betten (eigene)
* - Betten (Verband)
* - Zellen (eigene)
* - Zellen (Verband)
*/
(function () {
let buildings;
let allianceBuildings;
function addMenuEntry() {
const divider = document.createElement("li");
divider.setAttribute("class", "divider");
divider.setAttribute("role", "presentation");
document.getElementById("logout_button").parentElement.parentElement.append(divider);
const bedIcon = document.createElement("span");
bedIcon.setAttribute("class", "glyphicon glyphicon-blackboard");
const button = document.createElement("a");
button.setAttribute("href", "javascript: void(0)");
button.setAttribute("id", "occupancy-button");
button.append(bedIcon);
button.append(" Belegungsanzeiger");
button.addEventListener("click", buttonClick);
const li = document.createElement("li");
li.appendChild(button);
document.getElementById("logout_button").parentElement.parentElement.append(li);
}
function addModal() {
const modal = document.createElement("div");
modal.className = "modal fade";
modal.id = "occupancy-modal";
modal.setAttribute("tabindex", "-1");
modal.setAttribute("role", "dialog");
modal.setAttribute("aria-labelledby", "occupancy-modal-label");
modal.setAttribute("aria-hidden", "true");
modal.style.display = "none";
modal.style.zIndex = "5000";
modal.innerHTML = `
<div class="modal-dialog modal-lg" role="document" style="width: 1280px;">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title" id="occupancy-modal-label"><span class="glyphicon glyphicon-blackboard" aria-hidden="true"></span> Belegungsanzeiger</h1>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" style="max-height: calc(100vh - 212px);overflow-y: auto;">
<div>
<!-- Summary -->
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Gebäudetyp</th>
<th>Eigene Gebäude</th>
<th>Verbandsgebäude</th>
</tr>
</thead>
<tbody>
<tr>
<td>Krankenhäuser</td>
<td id="summary-hospitals-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-hospitals-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>Zellen</td>
<td id="summary-cells-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-cells-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>Feuerwehrschulen</td>
<td id="summary-fire-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-fire-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>Rettungsschulen</td>
<td id="summary-rescue-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-rescue-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>Polizeischulen</td>
<td id="summary-police-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-police-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
<tr>
<tr>
<td>Schule für Seefahrt und Seenotrettung</td>
<td id="summary-search-and-rescue-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-search-and-rescue-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>THW Bundesschulen</td>
<td id="summary-technical-aid-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
<td id="summary-alliance-technical-aid-academies-occupancy">
<div class="row">
<div class="col-md-12">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<hr class="divider">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#hospitals-panel" aria-controls="hospitals-panel" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-bed" aria-hidden="true"></span> Krankenhäuser</a>
</li>
<li role="presentation">
<a href="#alliance-hospitals-panel" aria-controls="alliance-hospitals-panel" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-bed" aria-hidden="true"></span> Verbands-Krankenhäuser</a>
</li>
<li role="presentation">
<a href="#cells-panel" aria-controls="cells-panel" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Zellen</a>
</li>
<li role="presentation">
<a href="#alliance-cells-panel" aria-controls="alliance-cells-panel" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Verbandszellen</a>
</li>
<li role="presentation">
<a href="#schools-panel" aria-controls="schools-panel" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-blackboard" aria-hidden="true"></span> Schulen</a>
</li>
<li role="presentation">
<a href="#alliance-schools-panel" aria-controls="alliance-schools-panel" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-blackboard" aria-hidden="true"></span> Verbandsschulen</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="hospitals-panel">
<div id="hospitals-occupancy">
<div class="row">
<div class="col-md-12 bg">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="alliance-hospitals-panel">
<div id="alliance-hospitals-occupancy">
<div class="row">
<div class="col-md-12 bg">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="cells-panel">
<div class="row">
<div class="col-md-12 bg">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="alliance-cells-panel">
<div class="row">
<div class="col-md-12 bg">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="schools-panel">
<div class="row">
<div class="col-md-12 bg">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="alliance-schools-panel">
<div class="row">
<div class="col-md-12 bg">
<div class="loader">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
}
function addStyle() {
const style =
".loader{width:100px;height:100px;border-radius:100%;position:relative;margin:0 auto;top:40px;left:-2.5px}.loader span{display:inline-block;width:5px;height:20px;background-color:#c9302c}.loader span:first-child{animation:1s ease-in-out infinite grow}.loader span:nth-child(2){animation:1s ease-in-out .15s infinite grow}.loader span:nth-child(3){animation:1s ease-in-out .3s infinite grow}.loader span:nth-child(4){animation:1s ease-in-out .45s infinite grow}@keyframes grow{0%,100%{-webkit-transform:scaleY(1);-ms-transform:scaleY(1);-o-transform:scaleY(1);transform:scaleY(1)}50%{-webkit-transform:scaleY(1.8);-ms-transform:scaleY(1.8);-o-transform:scaleY(1.8);transform:scaleY(1.8)}}";
const styleElement = document.createElement("style");
styleElement.innerHTML = style;
document.head.appendChild(styleElement);
}
async function fetchBuildings() {
if (buildings) {
return buildings;
}
buildings = await fetch("/api/buildings")
.then(response => response.json())
.then(buildings => {
return buildings.sort((a, b) => {
if (a.caption < b.caption) return -1;
if (a.caption > b.caption) return 1;
return 0;
});
});
return buildings;
}
async function fetchAllianceBuildings() {
if (allianceBuildings) {
return allianceBuildings;
}
allianceBuildings = await fetch("/api/alliance_buildings")
.then(response => response.json())
.then(buildings => {
return buildings.sort((a, b) => {
if (a.caption < b.caption) return -1;
if (a.caption > b.caption) return 1;
return 0;
});
});
return allianceBuildings;
}
function renderProgressbarAbsolute(capacity, used) {
return renderProgressbar(capacity, used, used + "/" + capacity);
}
function renderProgressbarRelative(capacity, used) {
let occupancy = 0;
if (capacity > 0) {
occupancy = (used / capacity) * 100;
}
return renderProgressbar(capacity, used, Math.round(occupancy) + "%");
}
function renderProgressbar(capacity, used, innerText) {
const occupancy = (used / capacity) * 100;
let progressBarColor = "progress-bar-success";
if (occupancy >= 80) {
progressBarColor = "progress-bar-danger";
} else if (occupancy >= 40) {
progressBarColor = "progress-bar-warning";
}
const progressBarInner = document.createElement("div");
progressBarInner.classList.add("progress-bar");
progressBarInner.classList.add(progressBarColor);
progressBarInner.setAttribute("role", "progressbar");
progressBarInner.setAttribute("aria-valuenow", used);
progressBarInner.setAttribute("aria-valuemin", "0");
progressBarInner.setAttribute("aria-valuemax", capacity);
progressBarInner.setAttribute("style", "width: " + occupancy + "%;");
progressBarInner.innerText = innerText;
const progressBar = document.createElement("div");
progressBar.classList.add("progress");
progressBar.appendChild(progressBarInner);
return progressBar;
}
function renderHospitalsTable(hospitals) {
const hospitalTable = document.createElement("table");
hospitalTable.classList.add("table", "table-striped", "table-hover", "table-condensed");
const hospitalTableHeader = document.createElement("thead");
const hospitalTableHeaderRow = document.createElement("tr");
const hospitalTableHeaderCaption = document.createElement("th");
hospitalTableHeaderCaption.innerText = "Krankenhaus";
const hospitalTableHeaderLevel = document.createElement("th");
hospitalTableHeaderLevel.innerText = "Kapazität";
const hospitalTableHeaderAllianceShare = document.createElement("th");
hospitalTableHeaderAllianceShare.innerText = "Provision";
const hospitalTableHeaderOccupancy = document.createElement("th");
hospitalTableHeaderOccupancy.innerText = "Auslastung";
hospitalTableHeaderRow.appendChild(hospitalTableHeaderCaption);
hospitalTableHeaderRow.appendChild(hospitalTableHeaderLevel);
hospitalTableHeaderRow.appendChild(hospitalTableHeaderAllianceShare);
hospitalTableHeaderRow.appendChild(hospitalTableHeaderOccupancy);
hospitalTableHeader.appendChild(hospitalTableHeaderRow);
const hospitalTableBody = document.createElement("tbody");
hospitals.forEach(hospital => {
const capacity = hospital.level + 10;
let levelColor = "warning";
if (capacity === 30) {
levelColor = "success";
}
const capacitySpan = document.createElement("span");
capacitySpan.classList.add("label", "label-" + levelColor);
capacitySpan.innerText = capacity;
let creditsText = "Nicht geteilt";
let creditsClass = "warning";
if (hospital.hasOwnProperty("alliance_share_credits_percentage")) {
if (hospital.alliance_share_credits_percentage === 10) {
creditsClass = "success";
}
creditsText = hospital.alliance_share_credits_percentage + "%";
} else {
creditsClass = "danger";
}
const creditsSpan = document.createElement("span");
creditsSpan.classList.add("label", "label-" + creditsClass);
creditsSpan.innerText = creditsText;
const buildingLink = document.createElement("a");
buildingLink.href = "/buildings/" + hospital.id;
buildingLink.innerText = hospital.caption;
buildingLink.setAttribute("target", "_blank");
const hospitalTableRow = document.createElement("tr");
const hospitalTableRowCaption = document.createElement("td");
hospitalTableRowCaption.appendChild(buildingLink);
const hospitalTableRowLevel = document.createElement("td");
hospitalTableRowLevel.appendChild(capacitySpan);
const hospitalTableRowAllianceShare = document.createElement("td");
hospitalTableRowAllianceShare.appendChild(creditsSpan);
const hospitalTableRowOccupancy = document.createElement("td");
hospitalTableRowOccupancy.appendChild(renderProgressbarAbsolute(hospital.level + 10, hospital.patient_count));
hospitalTableRow.appendChild(hospitalTableRowCaption);
hospitalTableRow.appendChild(hospitalTableRowLevel);
hospitalTableRow.appendChild(hospitalTableRowAllianceShare);
hospitalTableRow.appendChild(hospitalTableRowOccupancy);
hospitalTableBody.appendChild(hospitalTableRow);
});
hospitalTable.appendChild(hospitalTableHeader);
hospitalTable.appendChild(hospitalTableBody);
return hospitalTable;
}
function calculateNumberOfHospitalsAndTotalCapacity(hospitals) {
let totalCapacity = 0;
hospitals.forEach(hospital => {
totalCapacity += hospital.level + 10;
});
return totalCapacity;
}
async function renderHospitals() {
await fetchBuildings()
.then(buildings => buildings.filter(building => building.building_type === 4))
.then(hospitals => {
const hospitalTable = renderHospitalsTable(hospitals);
const totalCapacity = calculateNumberOfHospitalsAndTotalCapacity(hospitals);
const totalPatients = hospitals.reduce((total, hospital) => {
return total + hospital.patient_count;
}, 0);
const freeCapacity = totalCapacity - totalPatients;
const infoParagraph = document.createElement("p");
infoParagraph.innerText =
freeCapacity.toLocaleString() + " von " + totalCapacity.toLocaleString() + " Betten sind frei";
const summaryHospitalsOccupancyDiv = document.getElementById("summary-hospitals-occupancy");
summaryHospitalsOccupancyDiv.innerHTML = "";
summaryHospitalsOccupancyDiv.appendChild(renderProgressbarRelative(totalCapacity, totalPatients));
summaryHospitalsOccupancyDiv.appendChild(infoParagraph);
const hospitalsOccupancyDiv = document.getElementById("hospitals-occupancy");
hospitalsOccupancyDiv.innerHTML = "";
hospitalsOccupancyDiv.appendChild(hospitalTable);
});
}
async function renderAllianceHospitals() {
await fetchAllianceBuildings()
.then(buildings => buildings.filter(building => building.building_type === 4))
.then(hospitals => {
const hospitalTable = renderHospitalsTable(hospitals);
const totalCapacity = calculateNumberOfHospitalsAndTotalCapacity(hospitals);
const totalPatients = hospitals.reduce((total, hospital) => {
return total + hospital.patient_count;
}, 0);
const freeCapacity = totalCapacity - totalPatients;
const infoParagraph = document.createElement("p");
infoParagraph.innerText =
freeCapacity.toLocaleString() + " von " + totalCapacity.toLocaleString() + " Betten sind frei";
const summaryHospitalsOccupancyDiv = document.getElementById("summary-alliance-hospitals-occupancy");
summaryHospitalsOccupancyDiv.innerHTML = "";
summaryHospitalsOccupancyDiv.appendChild(renderProgressbarRelative(totalCapacity, totalPatients));
summaryHospitalsOccupancyDiv.appendChild(infoParagraph);
const hospitalsOccupancyDiv = document.getElementById("alliance-hospitals-occupancy");
hospitalsOccupancyDiv.innerHTML = "";
hospitalsOccupancyDiv.appendChild(hospitalTable);
});
}
function renderCellsTable(buildings) {
const cellsTable = document.createElement("table");
cellsTable.classList.add("table", "table-striped");
const cellsTableHeaderRow = document.createElement("tr");
const cellsTableHeaderCaption = document.createElement("th");
cellsTableHeaderCaption.innerText = "Name";
const cellsTableHeaderCapacity = document.createElement("th");
cellsTableHeaderCapacity.innerText = "Kapazität";
const cellsTableHeaderProvision = document.createElement("th");
cellsTableHeaderProvision.innerText = "Provision";
const cellsTableHeaderOccupancy = document.createElement("th");
cellsTableHeaderOccupancy.innerText = "Auslastung";
const cellsTableHeader = document.createElement("thead");
cellsTableHeaderRow.appendChild(cellsTableHeaderCaption);
cellsTableHeaderRow.appendChild(cellsTableHeaderCapacity);
cellsTableHeaderRow.appendChild(cellsTableHeaderProvision);
cellsTableHeaderRow.appendChild(cellsTableHeaderOccupancy);
cellsTableHeader.appendChild(cellsTableHeaderRow);
const cellsTableBody = document.createElement("tbody");
buildings.forEach(building => {
let capacityClass = "warning";
if (building.smallBuilding === false && building.numberOfCells === 10) {
capacityClass = "success";
}
if (building.smallBuilding === true && building.numberOfCells === 2) {
capacityClass = "success";
}
const capacitySpan = document.createElement("span");
capacitySpan.classList.add("label", "label-" + capacityClass);
capacitySpan.innerText = building.numberOfCells;
const buildingLink = document.createElement("a");
buildingLink.href = "/buildings/" + building.id;
buildingLink.innerText = building.caption;
buildingLink.setAttribute("target", "_blank");
let creditsText = "Nicht geteilt";
let provisionClass = "warning";
if (building.provision !== undefined) {
if (building.provision === 10) {
provisionClass = "success";
}
creditsText = building.provision + "%";
} else {
provisionClass = "danger";
}
const provisionSpan = document.createElement("span");
provisionSpan.classList.add("label", "label-" + provisionClass);
provisionSpan.innerText = creditsText;
const cellsTableRowCaption = document.createElement("td");
cellsTableRowCaption.appendChild(buildingLink);
const cellsTableRowCapacity = document.createElement("td");
cellsTableRowCapacity.appendChild(capacitySpan);
const cellsTableRowProvision = document.createElement("td");
cellsTableRowProvision.appendChild(provisionSpan);
const cellsTableRowOccupancy = document.createElement("td");
cellsTableRowOccupancy.append(renderProgressbarAbsolute(building.numberOfCells, building.numberOfPrisoners));
const cellsTableRow = document.createElement("tr");
cellsTableRow.appendChild(cellsTableRowCaption);
cellsTableRow.appendChild(cellsTableRowCapacity);
cellsTableRow.appendChild(cellsTableRowProvision);
cellsTableRow.appendChild(cellsTableRowOccupancy);
cellsTableBody.appendChild(cellsTableRow);
});
cellsTable.appendChild(cellsTableHeader);
cellsTable.appendChild(cellsTableBody);
return cellsTable;
}
async function renderCells() {
// policeStations constitutes an array of objects which have the following structure:
// {
// id: 123456,
// caption: "Polizeistation",
// buildingType: 6, // Either 6 or 19
// numberOfCells: 10, // sum of all extensions of type id 0 through 9
// numberOfPrisoners: 5, // taken from prisoner_count of the building
// }
let policeStations = await fetchBuildings()
.then(buildings => buildings.filter(building => building.building_type === 6 || building.building_type === 19))
.then(policeStations => {
return policeStations.map(policeStation => {
let numberOfCells = policeStation.extensions.filter(
extension => extension.type_id >= 0 && extension.type_id <= 9,
).length;
return {
id: policeStation.id,
caption: policeStation.caption,
buildingType: policeStation.building_type,
smallBuilding: policeStation.small_building,
numberOfCells: numberOfCells,
numberOfPrisoners: policeStation.prisoner_count,
provision: policeStation.alliance_share_credits_percentage,
};
});
});
const totalCapacity = policeStations.reduce(
(accumulator, currentValue) => accumulator + currentValue.numberOfCells,
0,
);
const totalOccupancy = policeStations.reduce(
(accumulator, currentValue) => accumulator + currentValue.numberOfPrisoners,
0,
);
const freeCapacity = totalCapacity - totalOccupancy;
const infoParagraph = document.createElement("p");
infoParagraph.innerText =
freeCapacity.toLocaleString() + " von " + totalCapacity.toLocaleString() + " Zellen sind frei";
const summaryCellsOccupancyDiv = document.getElementById("summary-cells-occupancy");
summaryCellsOccupancyDiv.innerHTML = "";
summaryCellsOccupancyDiv.appendChild(renderProgressbarRelative(totalCapacity, totalOccupancy));
summaryCellsOccupancyDiv.appendChild(infoParagraph);
const cellsPanel = document.getElementById("cells-panel");
cellsPanel.innerHTML = "";
cellsPanel.appendChild(renderCellsTable(policeStations));
}
function renderAllianceCellsTable(policeStations) {
const cellsTable = document.createElement("table");
cellsTable.classList.add("table", "table-bordered", "table-striped");
const cellsTableHeaderRow = document.createElement("tr");
const cellsTableHeaderCaption = document.createElement("th");
cellsTableHeaderCaption.innerText = "Gebäude";
const cellsTableHeaderCapacity = document.createElement("th");
cellsTableHeaderCapacity.innerText = "Kapazität";
const cellsTableHeaderProvision = document.createElement("th");
cellsTableHeaderProvision.innerText = "Provision";
const cellsTableHeaderOccupancy = document.createElement("th");
cellsTableHeaderOccupancy.innerText = "Auslastung";
const cellsTableHeader = document.createElement("thead");
cellsTableHeaderRow.appendChild(cellsTableHeaderCaption);
cellsTableHeaderRow.appendChild(cellsTableHeaderCapacity);
cellsTableHeaderRow.appendChild(cellsTableHeaderProvision);
cellsTableHeaderRow.appendChild(cellsTableHeaderOccupancy);
cellsTableHeader.appendChild(cellsTableHeaderRow);
const cellsTableBody = document.createElement("tbody");
policeStations.forEach(building => {
let capacityClass = "warning";
if (building.smallBuilding === false && building.numberOfCells === 10) {
capacityClass = "success";
}
if (building.smallBuilding === true && building.numberOfCells === 2) {
capacityClass = "success";
}
const capacitySpan = document.createElement("span");
capacitySpan.classList.add("label", "label-" + capacityClass);
capacitySpan.innerText = building.numberOfCells;
const buildingLink = document.createElement("a");
buildingLink.href = "/buildings/" + building.id;
buildingLink.innerText = building.caption;
buildingLink.setAttribute("target", "_blank");
let provisionClass = "warning";
if (building.provision === 0) {
provisionClass = "success";
}
const provisionSpan = document.createElement("span");
provisionSpan.classList.add("label", "label-" + provisionClass);
provisionSpan.innerText = building.provision;
if (!isNaN(building.provision)) {
provisionSpan.innerText += "%";
}
const cellsTableRowCaption = document.createElement("td");
cellsTableRowCaption.appendChild(buildingLink);
const cellsTableRowCapacity = document.createElement("td");
cellsTableRowCapacity.appendChild(capacitySpan);
const cellsTableRowProvision = document.createElement("td");
cellsTableRowProvision.appendChild(provisionSpan);
const cellsTableRowOccupancy = document.createElement("td");
cellsTableRowOccupancy.appendChild(renderProgressbarAbsolute(building.numberOfCells, building.numberOfPrisoners));
const cellsTableRow = document.createElement("tr");
cellsTableRow.appendChild(cellsTableRowCaption);
cellsTableRow.appendChild(cellsTableRowCapacity);
cellsTableRow.appendChild(cellsTableRowProvision);
cellsTableRow.appendChild(cellsTableRowOccupancy);
cellsTableBody.appendChild(cellsTableRow);
});
cellsTable.appendChild(cellsTableHeader);
cellsTable.appendChild(cellsTableBody);
return cellsTable;
}
async function renderAllianceCells() {
let alliancePoliceStations = await fetchAllianceBuildings()
.then(buildings => buildings.filter(building => building.building_type === 16))
.then(policeStations => {
return policeStations.map(policeStation => {
let numberOfCells = policeStation.extensions.filter(
extension => extension.type_id >= 0 && extension.type_id <= 9,
).length;
return {
id: policeStation.id,
caption: policeStation.caption,
buildingType: policeStation.building_type,
smallBuilding: policeStation.small_building,
numberOfCells: numberOfCells,
numberOfPrisoners: policeStation.prisoner_count,
provision: policeStation.alliance_share_credits_percentage,
};
});
});
const totalCapacity = alliancePoliceStations.reduce(
(accumulator, currentValue) => accumulator + currentValue.numberOfCells,
0,
);
const totalOccupancy = alliancePoliceStations.reduce(
(accumulator, currentValue) => accumulator + currentValue.numberOfPrisoners,
0,
);
const freeCapacity = totalCapacity - totalOccupancy;
const infoParagraph = document.createElement("p");
infoParagraph.innerText =
freeCapacity.toLocaleString() + " von " + totalCapacity.toLocaleString() + " Zellen sind frei";
const summaryCellsOccupancyDiv = document.getElementById("summary-alliance-cells-occupancy");
summaryCellsOccupancyDiv.innerHTML = "";
summaryCellsOccupancyDiv.appendChild(renderProgressbarRelative(totalCapacity, totalOccupancy));
summaryCellsOccupancyDiv.appendChild(infoParagraph);
const cellsPanel = document.getElementById("alliance-cells-panel");
cellsPanel.innerHTML = "";
cellsPanel.appendChild(renderAllianceCellsTable(alliancePoliceStations));
}
function renderSchoolsTable(schools, name) {
const schoolsTable = document.createElement("table");
schoolsTable.classList.add("table", "table-bordered", "table-striped");
const schoolsTableHeaderRow = document.createElement("tr");
const schoolsTableHeaderCaption = document.createElement("th");
schoolsTableHeaderCaption.innerText = name;
const schoolsTableHeaderCapacity = document.createElement("th");
schoolsTableHeaderCapacity.innerText = "Kapazität";
const schoolsTableHeaderOccupancy = document.createElement("th");
schoolsTableHeaderOccupancy.innerText = "Auslastung";
const schoolsTableHeader = document.createElement("thead");
schoolsTableHeaderRow.appendChild(schoolsTableHeaderCaption);
schoolsTableHeaderRow.appendChild(schoolsTableHeaderCapacity);
schoolsTableHeaderRow.appendChild(schoolsTableHeaderOccupancy);
schoolsTableHeader.appendChild(schoolsTableHeaderRow);
const schoolsTableBody = document.createElement("tbody");
schools.forEach(building => {
const buildingLink = document.createElement("a");
buildingLink.href = "/buildings/" + building.id;
buildingLink.innerText = building.caption;
buildingLink.setAttribute("target", "_blank");
let capacityClass = "warning";
if (building.capacity === 4) {
capacityClass = "success";
}
let capacitySpan = document.createElement("span");
capacitySpan.classList.add("label", "label-" + capacityClass);
capacitySpan.innerText = building.capacity;
const schoolsTableRowCaption = document.createElement("td");
schoolsTableRowCaption.appendChild(buildingLink);
const schoolsTableRowCapacity = document.createElement("td");
schoolsTableRowCapacity.appendChild(capacitySpan);
const schoolsTableRowOccupancy = document.createElement("td");
schoolsTableRowOccupancy.appendChild(renderProgressbarAbsolute(building.capacity, building.occupancy));
const schoolsTableRow = document.createElement("tr");
schoolsTableRow.appendChild(schoolsTableRowCaption);
schoolsTableRow.appendChild(schoolsTableRowCapacity);
schoolsTableRow.appendChild(schoolsTableRowOccupancy);
schoolsTableBody.appendChild(schoolsTableRow);
});
schoolsTable.appendChild(schoolsTableHeader);
schoolsTable.appendChild(schoolsTableBody);
return schoolsTable;
}
async function getSchoolsMeta(buildingTypeId) {
return await fetchBuildings()
.then(buildings => buildings.filter(building => building.building_type === buildingTypeId))
.then(schools => {
return schools.map(school => {
let numberOfRooms = school.extensions.filter(
extension => extension.type_id >= 0 && extension.type_id <= 2,
).length;
return {
id: school.id,
caption: school.caption,
buildingType: school.building_type,
capacity: 1 + numberOfRooms,
occupancy: school.schoolings.length,
provision: school.alliance_share_credits_percentage,
};
});
});
}
async function renderSchools() {
const schoolTypes = [
{ id: 1, name: "Feuerwehrschulen", divId: "summary-fire-academies-occupancy" },
{ id: 3, name: "Rettungsschulen", divId: "summary-rescue-academies-occupancy" },
{ id: 8, name: "Polizeischulen", divId: "summary-police-academies-occupancy" },
{ id: 10, name: "THW Bundesschulen", divId: "summary-technical-aid-academies-occupancy" },
{ id: 27, name: "Schule für Seefahrt und Seenotrettung", divId: "summary-search-and-rescue-academies-occupancy" },
];
const schoolsPanel = document.getElementById("schools-panel");
schoolsPanel.innerHTML = "";
for (const schoolType of schoolTypes) {
let schools = await getSchoolsMeta(schoolType.id);
const totalCapacity = schools.reduce((accumulator, currentValue) => accumulator + currentValue.capacity, 0);
const totalOccupancy = schools.reduce((accumulator, currentValue) => accumulator + currentValue.occupancy, 0);
const freeCapacity = totalCapacity - totalOccupancy;
const infoParagraph = document.createElement("p");
infoParagraph.innerText =
freeCapacity.toLocaleString() + " von " + totalCapacity.toLocaleString() + " Klassenräumen sind frei";
const summaryCellsOccupancyDiv = document.getElementById(schoolType.divId);
summaryCellsOccupancyDiv.innerHTML = "";
summaryCellsOccupancyDiv.appendChild(renderProgressbarRelative(totalCapacity, totalOccupancy));
summaryCellsOccupancyDiv.appendChild(infoParagraph);
schoolsPanel.appendChild(renderSchoolsTable(schools, schoolType.name));
}
}
async function getAllianceSchoolsMeta(buildingTypeId) {
return await fetchAllianceBuildings()
.then(buildings => buildings.filter(building => building.building_type === buildingTypeId))
.then(schools => {
return schools.map(school => {
let numberOfRooms = school.extensions.filter(
extension => extension.type_id >= 0 && extension.type_id <= 2,
).length;
return {
id: school.id,
caption: school.caption,
buildingType: school.building_type,
capacity: 1 + numberOfRooms,
// Return school.schoolings.length as occupancy
// if school.schoolings is undefined, apply 0
occupancy: school.schoolings ? school.schoolings.length : 0,
provision: school.alliance_share_credits_percentage,
};
});
});
}
async function renderAllianceSchools() {
const schoolTypes = [
{ id: 1, name: "Feuerwehrschulen", divId: "summary-alliance-fire-academies-occupancy" },
{ id: 3, name: "Rettungsschulen", divId: "summary-alliance-rescue-academies-occupancy" },
{ id: 8, name: "Polizeischulen", divId: "summary-alliance-police-academies-occupancy" },
{ id: 10, name: "THW Bundesschulen", divId: "summary-alliance-technical-aid-academies-occupancy" },
{
id: 27,
name: "Schule für Seefahrt und Seenotrettung",
divId: "summary-alliance-search-and-rescue-academies-occupancy",
},
];
const schoolsPanel = document.getElementById("alliance-schools-panel");
schoolsPanel.innerHTML = "";
for (const schoolType of schoolTypes) {
let allianceSchools = await getAllianceSchoolsMeta(schoolType.id);
const totalCapacity = allianceSchools.reduce(
(accumulator, currentValue) => accumulator + currentValue.capacity,
0,
);
const totalOccupancy = allianceSchools.reduce(
(accumulator, currentValue) => accumulator + currentValue.occupancy,
0,
);
const freeCapacity = totalCapacity - totalOccupancy;
const infoParagraph = document.createElement("p");
infoParagraph.innerText =
freeCapacity.toLocaleString() + " von " + totalCapacity.toLocaleString() + " Klassenräumen sind frei";
const summarySchoolsOccupancyDiv = document.getElementById(schoolType.divId);
summarySchoolsOccupancyDiv.innerHTML = "";
summarySchoolsOccupancyDiv.appendChild(renderProgressbarRelative(totalCapacity, totalOccupancy));
summarySchoolsOccupancyDiv.appendChild(infoParagraph);
schoolsPanel.appendChild(renderSchoolsTable(allianceSchools, schoolType.name));
}
}
function buttonClick(event) {
event.preventDefault();
$("#occupancy-modal").modal("show");
renderHospitals();
renderAllianceHospitals();
renderCells();
renderAllianceCells();
renderSchools();
renderAllianceSchools();
}
function main() {
addStyle();
addModal();
addMenuEntry();
}
main();
})();