refractored all var to const or let
This commit was merged in pull request #6.
This commit is contained in:
164
public/js/app.js
164
public/js/app.js
@@ -14,13 +14,13 @@
|
||||
// =====================================================================
|
||||
|
||||
// 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 = {
|
||||
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' },
|
||||
@@ -31,14 +31,14 @@ var CATEGORIES = {
|
||||
};
|
||||
|
||||
// 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', {
|
||||
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 = {
|
||||
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);
|
||||
@@ -144,11 +144,11 @@ L.Control.geocoder({
|
||||
// }).addTo(map);
|
||||
|
||||
// Mouse Position Display
|
||||
// var MousePositionControl = L.Control.extend({
|
||||
// const MousePositionControl = L.Control.extend({
|
||||
// options: { position: 'bottomright' },
|
||||
|
||||
// onAdd: function () {
|
||||
// var container = L.DomUtil.create('div', 'mouse-position-display');
|
||||
// const container = L.DomUtil.create('div', 'mouse-position-display');
|
||||
// container.innerHTML = 'Lat: , Lng: ';
|
||||
|
||||
// map.on('mousemove', function (e) {
|
||||
@@ -162,12 +162,12 @@ L.Control.geocoder({
|
||||
// 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]);
|
||||
}
|
||||
|
||||
@@ -338,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,
|
||||
@@ -351,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,
|
||||
@@ -368,17 +368,17 @@ 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">' + categoryIcon(cat) + ' ' + cat.label + '</span>' +
|
||||
'<div class="popup-detail-title">' + escapeHtml(props.title) + '</div>' +
|
||||
@@ -418,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) {
|
||||
@@ -478,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',
|
||||
@@ -570,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
|
||||
@@ -619,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 ||
|
||||
@@ -647,11 +647,11 @@ 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 + ')">' +
|
||||
@@ -708,12 +708,12 @@ 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;">' +
|
||||
@@ -727,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) {
|
||||
@@ -737,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);
|
||||
@@ -763,21 +763,21 @@ 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;">' +
|
||||
categoryIcon(cat) + ' ' +
|
||||
@@ -796,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';
|
||||
}
|
||||
@@ -816,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;
|
||||
@@ -880,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');
|
||||
@@ -911,7 +911,7 @@ 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;
|
||||
}
|
||||
@@ -928,10 +928,10 @@ function categoryIcon(cat) {
|
||||
|
||||
// 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.label;
|
||||
option.dataset.icon = cat.faIcon;
|
||||
|
||||
Reference in New Issue
Block a user