Compare commits
6 Commits
f8f0d514bb
...
2c02a61791
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c02a61791 | |||
| a38cf999f2 | |||
| 78bdc22781 | |||
| f810ed520c | |||
| 2b3fcb6ebf | |||
| 5fe7522f5f |
@@ -54,7 +54,7 @@ if (!$municipality) {
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder@2.4.0/dist/Control.Geocoder.css">
|
||||
|
||||
<!-- Leaflet Polyline Measurement Tool -->
|
||||
<link rel="stylesheet" href="https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.css">
|
||||
<!-- <link rel="stylesheet" href="https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.css"> -->
|
||||
|
||||
<!-- Font Awesome 6 for Icons -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
@@ -321,7 +321,7 @@ if (!$municipality) {
|
||||
<script src="https://unpkg.com/leaflet-control-geocoder@2.4.0/dist/Control.Geocoder.min.js"></script>
|
||||
|
||||
<!-- Leaflet PolylineMeasure -->
|
||||
<script src="https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.js"></script>
|
||||
<!-- <script src="https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.js"></script> -->
|
||||
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.14.0/dist/sweetalert2.all.min.js"></script>
|
||||
|
||||
242
public/js/app.js
242
public/js/app.js
@@ -14,31 +14,31 @@
|
||||
// =====================================================================
|
||||
|
||||
// API Endpoint as relative Path
|
||||
var API_URL = 'api/contributions.php';
|
||||
const API_URL = 'api/contributions.php';
|
||||
|
||||
// Current User Name, set via Login Modal, stored in sessionStorage
|
||||
var currentUser = sessionStorage.getItem('webgis_user') || '';
|
||||
let currentUser = sessionStorage.getItem('webgis_user') || '';
|
||||
|
||||
// Category Definitions with Labels, Icons, and Colors
|
||||
var CATEGORIES = {
|
||||
mobility: { label: 'Mobilität', icon: '🚲', color: '#1565C0', faIcon: 'fa-bicycle' },
|
||||
building: { label: 'Bauen', icon: '🏗️', color: '#E65100', faIcon: 'fa-helmet-safety' },
|
||||
energy: { label: 'Energie', icon: '⚡', color: '#F9A825', faIcon: 'fa-bolt' },
|
||||
environment: { label: 'Umwelt', icon: '🌳', color: '#2E7D32', faIcon: 'fa-tree' },
|
||||
industry: { label: 'Industrie', icon: '🏭', color: '#6A1B9A', faIcon: 'fa-industry' },
|
||||
consumption: { label: 'Konsum', icon: '🛒', color: '#AD1457', faIcon: 'fa-cart-shopping' },
|
||||
other: { label: 'Sonstiges', icon: '📌', color: '#546E7A', faIcon: 'fa-map-pin' }
|
||||
const CATEGORIES = {
|
||||
consumption: { label: 'Geschäfte', faIcon: 'fa-cart-shopping', color: '#C00000' },
|
||||
building: { label: 'Bauen', faIcon: 'fa-building', color: '#E65100' },
|
||||
energy: { label: 'Energie', faIcon: 'fa-bolt', color: '#FFC000' },
|
||||
environment: { label: 'Umwelt', faIcon: 'fa-seedling', color: '#92D050' },
|
||||
mobility: { label: 'Mobilität', faIcon: 'fa-bus', color: '#0070C0' },
|
||||
industry: { label: 'Industrie', faIcon: 'fa-industry', color: '#7030A0' },
|
||||
other: { label: 'Sonstiges', faIcon: 'fa-thumbtack', color: '#7F7F7F' }
|
||||
};
|
||||
|
||||
// Application State
|
||||
var map; // Leaflet Map Instance
|
||||
var sidebar; // Sidebar Instance
|
||||
var contributionsLayer; // GeoJSON Layer holding all Contributions
|
||||
var contributionsData = []; // Raw Contribution Data Array
|
||||
var activeFilters = Object.keys(CATEGORIES); // Active Category Filters
|
||||
var drawnGeometry = null; // Temporary Storage for Geometry drawn with Geoman
|
||||
var drawnGeomType = null; // Temporary Storage for Geometry Type
|
||||
var userVotes = {}; // Tracks User Votes
|
||||
let map; // Leaflet Map Instance
|
||||
let sidebar; // Sidebar Instance
|
||||
let contributionsLayer; // GeoJSON Layer holding all Contributions
|
||||
let contributionsData = []; // Raw Contribution Data Array
|
||||
let activeFilters = Object.keys(CATEGORIES); // Active Category Filters
|
||||
let drawnGeometry = null; // Temporary Storage for Geometry drawn with Geoman
|
||||
let drawnGeomType = null; // Temporary Storage for Geometry Type
|
||||
let userVotes = {}; // Tracks User Votes
|
||||
|
||||
// =====================================================================
|
||||
// Block 2: Map Initialization
|
||||
@@ -63,17 +63,17 @@ map = L.map('map', {
|
||||
// =====================================================================
|
||||
|
||||
// Basemap Tile Layers
|
||||
var basemapOSM = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
const basemapOSM = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
maxZoom: 20
|
||||
});
|
||||
|
||||
var basemapCartoDB = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
||||
attribution: '© <a href="https://carto.com/">CARTO</a>',
|
||||
const basemapCartoDB = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
||||
attribution: '© <a href="https://carto.com/">Carto</a>',
|
||||
maxZoom: 20
|
||||
});
|
||||
|
||||
var basemapSatellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
||||
const basemapSatellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
||||
attribution: '© <a href="https://www.esri.com/">Esri</a>',
|
||||
maxZoom: 20
|
||||
});
|
||||
@@ -82,15 +82,15 @@ var basemapSatellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/
|
||||
basemapCartoDB.addTo(map);
|
||||
|
||||
// Layer Control
|
||||
var basemaps = {
|
||||
'OpenStreetMap': basemapOSM,
|
||||
'CartoDB (hell)': basemapCartoDB,
|
||||
'Satellit (Esri)': basemapSatellite,
|
||||
const basemaps = {
|
||||
'<i class="fa-solid fa-map" style="color:#404040;"></i> Hintergrundkarte (farbe)': basemapOSM,
|
||||
'<i class="fa-solid fa-map" style="color:#404040;"></i> Hintergrundkarte (grau)': basemapCartoDB,
|
||||
'<i class="fa-solid fa-satellite" style="color:#404040;"></i> Satellitenbild': basemapSatellite,
|
||||
};
|
||||
|
||||
var overlays = {}; // Populated later with Contribution Layers
|
||||
const overlays = {}; // Populated later with Contribution Layers
|
||||
|
||||
var layerControl = L.control.layers(basemaps, overlays, {
|
||||
const layerControl = L.control.layers(basemaps, overlays, {
|
||||
position: 'topright',
|
||||
collapsed: true
|
||||
}).addTo(map);
|
||||
@@ -135,39 +135,39 @@ L.Control.geocoder({
|
||||
}).addTo(map);
|
||||
|
||||
// Polyline Measure Tool
|
||||
L.control.polylineMeasure({
|
||||
position: 'topright',
|
||||
unit: 'metres',
|
||||
showBearings: false,
|
||||
clearMeasurementsOnStop: false,
|
||||
showClearControl: true
|
||||
}).addTo(map);
|
||||
// L.control.polylineMeasure({
|
||||
// position: 'topright',
|
||||
// unit: 'metres',
|
||||
// showBearings: false,
|
||||
// clearMeasurementsOnStop: false,
|
||||
// showClearControl: true
|
||||
// }).addTo(map);
|
||||
|
||||
// Mouse Position Display
|
||||
var MousePositionControl = L.Control.extend({
|
||||
options: { position: 'bottomright' },
|
||||
// const MousePositionControl = L.Control.extend({
|
||||
// options: { position: 'bottomright' },
|
||||
|
||||
onAdd: function () {
|
||||
var container = L.DomUtil.create('div', 'mouse-position-display');
|
||||
container.innerHTML = 'Lat: , Lng: ';
|
||||
// onAdd: function () {
|
||||
// const container = L.DomUtil.create('div', 'mouse-position-display');
|
||||
// container.innerHTML = 'Lat: , Lng: ';
|
||||
|
||||
map.on('mousemove', function (e) {
|
||||
container.innerHTML = 'Lat: ' + e.latlng.lat.toFixed(5) + ', Lng: ' + e.latlng.lng.toFixed(5);
|
||||
});
|
||||
// map.on('mousemove', function (e) {
|
||||
// container.innerHTML = 'Lat: ' + e.latlng.lat.toFixed(5) + ', Lng: ' + e.latlng.lng.toFixed(5);
|
||||
// });
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
// return container;
|
||||
// }
|
||||
// });
|
||||
|
||||
new MousePositionControl().addTo(map);
|
||||
// new MousePositionControl().addTo(map);
|
||||
|
||||
// GPS Location Button
|
||||
var GpsControl = L.Control.extend({
|
||||
const GpsControl = L.Control.extend({
|
||||
options: { position: 'topright' },
|
||||
|
||||
onAdd: function () {
|
||||
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
|
||||
var button = L.DomUtil.create('a', 'gps-control-button', container);
|
||||
const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
|
||||
const button = L.DomUtil.create('a', 'gps-control-button', container);
|
||||
button.href = '#';
|
||||
button.title = 'Mein Standort';
|
||||
button.innerHTML = '<i class="fa-solid fa-location-crosshairs"></i>';
|
||||
@@ -184,7 +184,7 @@ var GpsControl = L.Control.extend({
|
||||
new GpsControl().addTo(map);
|
||||
|
||||
// GPS Location Found Handler
|
||||
var gpsMarker = null;
|
||||
let gpsMarker = null;
|
||||
|
||||
map.on('locationfound', function (e) {
|
||||
if (gpsMarker) {
|
||||
@@ -240,7 +240,7 @@ map.pm.setLang('de');
|
||||
|
||||
// Captures drawn Geometry and opens the Create Modal
|
||||
map.on('pm:create', function (e) {
|
||||
var geojson = e.layer.toGeoJSON().geometry;
|
||||
const geojson = e.layer.toGeoJSON().geometry;
|
||||
|
||||
// Determines drawn Geometry Type and normalizes to simple Types
|
||||
if (e.shape === 'Marker') {
|
||||
@@ -280,8 +280,8 @@ map.on('pm:create', function (e) {
|
||||
|
||||
// Generic API Call Function
|
||||
function apiCall(data, callback) {
|
||||
var formData = new FormData();
|
||||
for (var key in data) {
|
||||
const formData = new FormData();
|
||||
for (const key in data) {
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
|
||||
@@ -324,8 +324,7 @@ function loadContributions() {
|
||||
onEachFeature: bindFeaturePopup
|
||||
}).addTo(map);
|
||||
|
||||
layerControl.addOverlay(contributionsLayer, 'Beiträge');
|
||||
|
||||
layerControl.addOverlay(contributionsLayer, '<i class="fa-solid fa-map-pin" style="color:#C00000;"></i> Beiträge');
|
||||
// Update Sidebar List and Statistics
|
||||
updateContributionsList();
|
||||
updateStatistics();
|
||||
@@ -339,7 +338,7 @@ function loadContributions() {
|
||||
|
||||
// Style for Point Features (CircleMarkers)
|
||||
function stylePoint(feature, latlng) {
|
||||
var cat = CATEGORIES[feature.properties.category] || CATEGORIES.other;
|
||||
const cat = CATEGORIES[feature.properties.category] || CATEGORIES.other;
|
||||
|
||||
return L.circleMarker(latlng, {
|
||||
radius: 8,
|
||||
@@ -352,7 +351,7 @@ function stylePoint(feature, latlng) {
|
||||
|
||||
// Style for Line and Polygon Features
|
||||
function styleLinePolygon(feature) {
|
||||
var cat = CATEGORIES[feature.properties.category] || CATEGORIES.other;
|
||||
const cat = CATEGORIES[feature.properties.category] || CATEGORIES.other;
|
||||
|
||||
return {
|
||||
color: cat.color,
|
||||
@@ -369,19 +368,19 @@ function styleLinePolygon(feature) {
|
||||
// =====================================================================
|
||||
|
||||
function bindFeaturePopup(feature, layer) {
|
||||
var props = feature.properties;
|
||||
var cat = CATEGORIES[props.category] || CATEGORIES.other;
|
||||
const props = feature.properties;
|
||||
const cat = CATEGORIES[props.category] || CATEGORIES.other;
|
||||
|
||||
// Formats Date
|
||||
var date = new Date(props.created_at);
|
||||
var dateStr = date.toLocaleDateString('de-DE', {
|
||||
const date = new Date(props.created_at);
|
||||
const dateStr = date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit', month: '2-digit', year: 'numeric'
|
||||
});
|
||||
|
||||
// Builds Popup on Click
|
||||
var html = '' +
|
||||
const html = '' +
|
||||
'<div class="popup-detail">' +
|
||||
'<span class="popup-detail-category">' + cat.icon + ' ' + cat.label + '</span>' +
|
||||
'<span class="popup-detail-category">' + categoryIcon(cat) + ' ' + cat.label + '</span>' +
|
||||
'<div class="popup-detail-title">' + escapeHtml(props.title) + '</div>' +
|
||||
(props.description ? '<div class="popup-detail-description">' + escapeHtml(props.description) + '</div>' : '') +
|
||||
'<div class="popup-detail-meta">' +
|
||||
@@ -406,7 +405,7 @@ function bindFeaturePopup(feature, layer) {
|
||||
layer.bindPopup(html, { maxWidth: 320, minWidth: 240 });
|
||||
|
||||
// Builds Tooltip on Hover
|
||||
layer.bindTooltip(cat.icon + ' ' + escapeHtml(props.title), {
|
||||
layer.bindTooltip(categoryIcon(cat) + ' ' + escapeHtml(props.title), {
|
||||
direction: 'top',
|
||||
offset: [0, -10]
|
||||
});
|
||||
@@ -419,11 +418,11 @@ function bindFeaturePopup(feature, layer) {
|
||||
|
||||
// CREATE: Submits new Contributions from Modal
|
||||
function submitCreate() {
|
||||
var category = document.getElementById('create-category').value;
|
||||
var title = document.getElementById('create-title').value.trim();
|
||||
var description = document.getElementById('create-description').value.trim();
|
||||
var geom = document.getElementById('create-geom').value;
|
||||
var geomType = document.getElementById('create-geom-type').value;
|
||||
const category = document.getElementById('create-category').value;
|
||||
const title = document.getElementById('create-title').value.trim();
|
||||
const description = document.getElementById('create-description').value.trim();
|
||||
const geom = document.getElementById('create-geom').value;
|
||||
const geomType = document.getElementById('create-geom-type').value;
|
||||
|
||||
// Validates
|
||||
if (!category) {
|
||||
@@ -479,13 +478,13 @@ function closeCreateModal() {
|
||||
// UPDATE: Edits existing Contributions
|
||||
function editContribution(contributionId) {
|
||||
// Finds Contribution in local Data
|
||||
var contribution = contributionsData.find(function (f) {
|
||||
const contribution = contributionsData.find(function (f) {
|
||||
return f.properties.contribution_id === contributionId;
|
||||
});
|
||||
|
||||
if (!contribution) return;
|
||||
|
||||
var props = contribution.properties;
|
||||
const props = contribution.properties;
|
||||
|
||||
Swal.fire({
|
||||
title: 'Beitrag bearbeiten',
|
||||
@@ -571,10 +570,10 @@ function voteContribution(contributionId, voteType) {
|
||||
}
|
||||
|
||||
// Updates local Vote State
|
||||
var likeBtn = document.getElementById('vote-like-' + contributionId);
|
||||
var dislikeBtn = document.getElementById('vote-dislike-' + contributionId);
|
||||
var likesSpan = document.getElementById('likes-' + contributionId);
|
||||
var dislikesSpan = document.getElementById('dislikes-' + contributionId);
|
||||
const likeBtn = document.getElementById('vote-like-' + contributionId);
|
||||
const dislikeBtn = document.getElementById('vote-dislike-' + contributionId);
|
||||
const likesSpan = document.getElementById('likes-' + contributionId);
|
||||
const dislikesSpan = document.getElementById('dislikes-' + contributionId);
|
||||
|
||||
if (response.action === 'created') {
|
||||
// New Vote — Highlights Button and updates Count
|
||||
@@ -620,16 +619,16 @@ function voteContribution(contributionId, voteType) {
|
||||
// =====================================================================
|
||||
|
||||
function updateContributionsList() {
|
||||
var container = document.getElementById('contributions-list');
|
||||
var searchInput = document.getElementById('list-search-input');
|
||||
var searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
|
||||
const container = document.getElementById('contributions-list');
|
||||
const searchInput = document.getElementById('list-search-input');
|
||||
const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
|
||||
|
||||
// Filters by Categories and Search Term
|
||||
var filtered = contributionsData.filter(function (f) {
|
||||
var props = f.properties;
|
||||
var matchesCategory = activeFilters.indexOf(props.category) !== -1;
|
||||
var cat = CATEGORIES[props.category] || CATEGORIES.other;
|
||||
var matchesSearch = !searchTerm ||
|
||||
const filtered = contributionsData.filter(function (f) {
|
||||
const props = f.properties;
|
||||
const matchesCategory = activeFilters.indexOf(props.category) !== -1;
|
||||
const cat = CATEGORIES[props.category] || CATEGORIES.other;
|
||||
const matchesSearch = !searchTerm ||
|
||||
props.title.toLowerCase().indexOf(searchTerm) !== -1 ||
|
||||
(props.description && props.description.toLowerCase().indexOf(searchTerm) !== -1) ||
|
||||
props.author_name.toLowerCase().indexOf(searchTerm) !== -1 ||
|
||||
@@ -648,16 +647,16 @@ function updateContributionsList() {
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
let html = '';
|
||||
filtered.forEach(function (f) {
|
||||
var props = f.properties;
|
||||
var cat = CATEGORIES[props.category] || CATEGORIES.other;
|
||||
var date = new Date(props.created_at).toLocaleDateString('de-DE');
|
||||
const props = f.properties;
|
||||
const cat = CATEGORIES[props.category] || CATEGORIES.other;
|
||||
const date = new Date(props.created_at).toLocaleDateString('de-DE');
|
||||
|
||||
html += '' +
|
||||
'<div class="contribution-card" onclick="flyToContribution(' + props.contribution_id + ')">' +
|
||||
'<div class="contribution-card-header">' +
|
||||
'<span class="contribution-card-category">' + cat.icon + ' ' + cat.label + '</span>' +
|
||||
'<span class="contribution-card-category">' + categoryIcon(cat) + ' ' + cat.label + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="contribution-card-title">' + escapeHtml(props.title) + '</div>' +
|
||||
'<div class="contribution-card-meta">' +
|
||||
@@ -709,18 +708,17 @@ document.getElementById('list-search-input').addEventListener('input', function
|
||||
|
||||
// Builds Category Filter Checkboxes
|
||||
function buildCategoryFilter() {
|
||||
var container = document.getElementById('category-filter');
|
||||
var html = '';
|
||||
const container = document.getElementById('category-filter');
|
||||
let html = '';
|
||||
|
||||
for (var key in CATEGORIES) {
|
||||
var cat = CATEGORIES[key];
|
||||
var checked = activeFilters.indexOf(key) !== -1 ? 'checked' : '';
|
||||
for (const key in CATEGORIES) {
|
||||
const cat = CATEGORIES[key];
|
||||
const checked = activeFilters.indexOf(key) !== -1 ? 'checked' : '';
|
||||
|
||||
html += '' +
|
||||
'<label style="display:flex;align-items:center;gap:8px;margin-bottom:6px;cursor:pointer;">' +
|
||||
'<input type="checkbox" value="' + key + '" ' + checked + ' onchange="toggleCategoryFilter(this)">' +
|
||||
'<span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:' + cat.color + ';"></span>' +
|
||||
'<span>' + cat.icon + ' ' + cat.label + '</span>' +
|
||||
'<span>' + categoryIcon(cat) + ' ' + cat.label + '</span>' +
|
||||
'</label>';
|
||||
}
|
||||
|
||||
@@ -729,7 +727,7 @@ function buildCategoryFilter() {
|
||||
|
||||
// Toggles a Category Filter on or off
|
||||
function toggleCategoryFilter(checkbox) {
|
||||
var category = checkbox.value;
|
||||
const category = checkbox.value;
|
||||
|
||||
if (checkbox.checked) {
|
||||
if (activeFilters.indexOf(category) === -1) {
|
||||
@@ -739,11 +737,11 @@ function toggleCategoryFilter(checkbox) {
|
||||
activeFilters = activeFilters.filter(function (c) { return c !== category; });
|
||||
}
|
||||
|
||||
// Refilters Map Layer
|
||||
// Refilters Map Layer
|
||||
if (contributionsLayer) {
|
||||
contributionsLayer.eachLayer(function (layer) {
|
||||
if (layer.feature) {
|
||||
var cat = layer.feature.properties.category;
|
||||
const cat = layer.feature.properties.category;
|
||||
if (activeFilters.indexOf(cat) !== -1) {
|
||||
layer.setStyle({ opacity: 1, fillOpacity: layer.feature.geometry.type === 'Point' ? 0.9 : 0.25 });
|
||||
if (layer.setRadius) layer.setRadius(8);
|
||||
@@ -765,24 +763,24 @@ function toggleCategoryFilter(checkbox) {
|
||||
|
||||
// Updates Statistics in Home Tab
|
||||
function updateStatistics() {
|
||||
var container = document.getElementById('stats-container');
|
||||
var total = contributionsData.length;
|
||||
const container = document.getElementById('stats-container');
|
||||
const total = contributionsData.length;
|
||||
|
||||
// Counts per Category
|
||||
var counts = {};
|
||||
const counts = {};
|
||||
contributionsData.forEach(function (f) {
|
||||
var cat = f.properties.category;
|
||||
const cat = f.properties.category;
|
||||
counts[cat] = (counts[cat] || 0) + 1;
|
||||
});
|
||||
|
||||
var html = '<p style="font-size:0.9rem;"><strong>' + total + '</strong> Beiträge insgesamt</p>';
|
||||
let html = '<p style="font-size:0.9rem;"><strong>' + total + '</strong> Beiträge insgesamt</p>';
|
||||
|
||||
for (var key in CATEGORIES) {
|
||||
var cat = CATEGORIES[key];
|
||||
var count = counts[key] || 0;
|
||||
for (const key in CATEGORIES) {
|
||||
const cat = CATEGORIES[key];
|
||||
const count = counts[key] || 0;
|
||||
if (count > 0) {
|
||||
html += '<div style="display:flex;align-items:center;gap:8px;margin:4px 0;font-size:0.85rem;">' +
|
||||
'<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:' + cat.color + ';"></span>' +
|
||||
categoryIcon(cat) + ' ' +
|
||||
cat.label + ': ' + count +
|
||||
'</div>';
|
||||
}
|
||||
@@ -798,7 +796,7 @@ function updateStatistics() {
|
||||
|
||||
// Welcome Modal shows on new Visits
|
||||
function checkWelcomeModal() {
|
||||
var hasVisited = localStorage.getItem('webgis_welcomed');
|
||||
const hasVisited = localStorage.getItem('webgis_welcomed');
|
||||
if (!hasVisited) {
|
||||
document.getElementById('welcome-modal').style.display = 'flex';
|
||||
}
|
||||
@@ -818,7 +816,7 @@ function showLoginModal() {
|
||||
}
|
||||
|
||||
function submitLogin() {
|
||||
var name = document.getElementById('user-name-input').value.trim();
|
||||
const name = document.getElementById('user-name-input').value.trim();
|
||||
if (!name) {
|
||||
Swal.fire('Name eingeben', 'Bitte geben Sie Ihren Namen ein.', 'warning');
|
||||
return;
|
||||
@@ -882,14 +880,14 @@ function showImprintModal() {
|
||||
// =====================================================================
|
||||
|
||||
function toggleMobileNav() {
|
||||
var nav = document.querySelector('.header-nav');
|
||||
const nav = document.querySelector('.header-nav');
|
||||
nav.classList.toggle('open');
|
||||
}
|
||||
|
||||
// Closes Mobile Nav when clicking outside
|
||||
document.addEventListener('click', function (e) {
|
||||
var nav = document.querySelector('.header-nav');
|
||||
var toggle = document.querySelector('.header-menu-toggle');
|
||||
const nav = document.querySelector('.header-nav');
|
||||
const toggle = document.querySelector('.header-menu-toggle');
|
||||
|
||||
if (nav.classList.contains('open') && !nav.contains(e.target) && !toggle.contains(e.target)) {
|
||||
nav.classList.remove('open');
|
||||
@@ -913,11 +911,16 @@ document.addEventListener('keydown', function (e) {
|
||||
// Escapes HTML to prevent Cross-Site Scripting (XSS) in Popups and Lists
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
var div = document.createElement('div');
|
||||
const div = document.createElement('div');
|
||||
div.appendChild(document.createTextNode(text));
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Returns a colored Font Awesome Icon HTML String for a Category
|
||||
function categoryIcon(cat) {
|
||||
return '<i class="fa-solid ' + cat.faIcon + '" style="color:' + cat.color + ';"></i>';
|
||||
}
|
||||
|
||||
|
||||
// =====================================================================
|
||||
// Block 16: Application Startup
|
||||
@@ -925,12 +928,13 @@ function escapeHtml(text) {
|
||||
|
||||
// Populates Category Dropdown in Create Modal from Categories Object
|
||||
function buildCategoryDropdown() {
|
||||
var select = document.getElementById('create-category');
|
||||
for (var key in CATEGORIES) {
|
||||
var cat = CATEGORIES[key];
|
||||
var option = document.createElement('option');
|
||||
const select = document.getElementById('create-category');
|
||||
for (const key in CATEGORIES) {
|
||||
const cat = CATEGORIES[key];
|
||||
const option = document.createElement('option');
|
||||
option.value = key;
|
||||
option.textContent = cat.icon + ' ' + cat.label;
|
||||
option.textContent = cat.label;
|
||||
option.dataset.icon = cat.faIcon;
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user