First Commit

This commit is contained in:
2025-11-02 22:52:08 +01:00
commit 73fbbf1be2
5821 changed files with 977526 additions and 0 deletions

View File

@@ -0,0 +1,546 @@
// calendar.js
document.addEventListener('DOMContentLoaded', async function () {
const months_short = [0, 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstg', 'Freitag', 'Samstag', 'Sonntag'];
const url = '../DB4js.php';
const default_dauer = 90; // Standardführung dauert 90min
let fuehrungszeiten;
let isedit;
let entryInfo;
// Von der Datenbank Werte abholen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const fetchFromDbase = async (body) => {
const response = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
// In die Datenbank Werte eintragen
// Param:
// body: Object mit cmd und param
// Return:
// angeforderte Daten als JSON
const putToDbase = async (body) => {
const response = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/js'},
body: JSON.stringify(body)
});
return await response.json();
}
// Die Daten der BEOs, die in einem Führungsteam sind, abholen
// Params: keine
// Retur:
// beos direkt gesetzt
async function getBEOs() {
let data = await fetchFromDbase({cmd: 'GET_BEOS', id: 'all', onlyguides: 'true'});
return data;
}
$('#versn').html("Version: " + VERSION + ' vom ' + VDATE)
let beos = await getBEOs();
// FullCalendar aufrufen und mit den Optionen versorgen
const calendarEl = document.getElementById('calendar');
let calendar = new FullCalendar.Calendar(calendarEl, {
themeSystem: 'bootstrap',
aspectRatio: 1.75,
locale: 'de',
editable: true,
selectable: true,
businessHours: {
daysOfWeek: [1, 2, 3, 4, 5],
startTime: '00:00',
endTime: '23.59'
},
nextDayThreshold: '00:30:00',
eventTimeFormat: {
hour: '2-digit',
minute: '2-digit',
meridiem: false
},
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
customButtons: {
anleitButton: {
text: "Anleitung",
click: function() {
showAnleitung();
}
}
},
headerToolbar: {
start: 'anleitButton',
center: 'title',
end: 'today prev next'
},
firstDay: 1,
buttonText: {
today: 'heute'
},
dayMaxEvents: true, // allow "more" link when too many events
eventSources: [
{
events: async function (info, success, failure) {
try {
let fdates = await getFdates(info.start, info.end, beos);
success(fdates);
} catch (err) {
console.log("Fehler gekommen:", err);
failure(err);
}
},
color: '#e3e839',
borderColor: '#e3e839',
textColor: 'black',
},
{
events: async function (info, success, failure) {
try {
let fdates = getSundays(info.start);
success(fdates);
} catch (err) {
console.log("Fehler gekommen:", err);
failure(err);
}
},
color: '#e3e839',
borderColor: '#e3e839',
textColor: 'black',
},
{
events: async function (info, success, failure) {
try {
let fdates = await getCaldates(info.start, info.end);
success(fdates);
} catch (err) {
console.log("Fehler gekommen:", err);
failure(err);
}
},
},
{
events: async function (info, success, failure) {
try {
let fdates = await getHolidays(info.start, info.end);
success(fdates);
} catch (err) {
console.log("Fehler gekommen:", err);
failure(err);
}
},
color: '#AAA',
borderColor: '#AAA',
textColor: 'black',
}
],
eventDisplay: 'block',
dateClick: function (info) {
entryInfo = info.event;
console.log("Dateclick ausgeführt");
isedit = false;
fillModal(info.date, null);
$('#myModal .modal-title').text('Neuer Eintrag');
$('#myModal').modal();
},
/*
eventMouseEnter: async function (info) {
isedit = true;
entryInfo = info.event;
let start = entryInfo.start;
const holiday = await checkInHoliday(start);
if (holiday == -1) {
$(info.el).popover({
container: 'body',
title: entryInfo.title,
content: (entryInfo._def.extendedProps.description !== undefined) ? getFuehrungsInfo(entryInfo) : getRegularInfo(entryInfo),
trigger: 'hover',
placement: 'auto',
html: true
});
$(info.el).popover('show');
}
},
*/
eventClick: function (info) {
isedit = true;
entryInfo = info.event;
showViewModal(entryInfo);
},
});
// *** Ende der Kalender-optionen
calendar.render();
// ************
// Button-Event-Handler für die Dialog-Boxen
// ************
$('#btn_delete').click(function () {
btnDeleteClicked(true);
});
$('#btn_edit').click(function () {
btnEditClicked();
});
$('#btn_saveentry').click(function () {
btnSaveClicked();
});
// 'Speichern' geklicked:
async function btnSaveClicked() {
let ready = await saveEntry();
if (isedit && ready) {
await deleteEntry(entryInfo._def.publicId, false);
}
calendar.refetchEvents();
console.log("gelöscht bei Edit");
}
// 'Löschen' geklicked
async function btnDeleteClicked(abfrage) {
$('#viewModal').modal('hide');
await deleteEntry(entryInfo._def.publicId, abfrage);
}
async function deleteEntry(id, abfrage) {
if (abfrage) {
if (!confirm('Wirklich löschen?')) {
return;
}
}
// let event = calendar.getEventById(id);
// event.remove();
$('#txtTitle').val('');
$('#txtBeschr').val('');
let ret = await putToDbase({cmd: 'DEL_CALENTRY', id: id});
if (ret != true) {
alert(`Fehler beim Löschen des Eintrages !`);
}
calendar.refetchEvents();
}
// 'Ändern' geklicked:
async function btnEditClicked() {
$('#viewModal').modal('hide');
$('#myModal .modal-title').text('Eintrag ändern');
$('#myModal').modal();
fillModal(entryInfo.start, entryInfo.end);
console.log(entryInfo.title, entryInfo._def.extendedProps.description);
$('#txtTitle').val(entryInfo.title);
$('#txtBeschr').val(entryInfo._def.extendedProps.description);
}
// Reaktion auf den 'Sichern'-Knopf
// Den Eintrag zusammen bauen, dann in die Datenbank eintragen und das
// Fenster wieder löschen
const saveEntry = async () => {
if ($('#txtTitle').val() == '') {
alert("Es muss ein Titel eingebeben werden!");
return false;
}
let entry = {};
entry.title = $('#txtTitle').val();
entry.description = $('#txtBeschr').val();
entry.start = $('#st_date_year option:selected').text() + '-';
entry.start += $('#st_date_month option:selected').val() + '-';
entry.start += $('#st_date_day option:selected').text() + 'T';
entry.start += $('#st_date_hours option:selected').text() + ':';
entry.start += $('#st_date_minutes option:selected').text();
entry.end = $('#end_date_year option:selected').text() + '-';
entry.end += $('#end_date_month option:selected').val() + '-';
entry.end += $('#end_date_day option:selected').text() + 'T';
entry.end += $('#end_date_hours option:selected').text() + ':';
entry.end += $('#end_date_minutes option:selected').text();
if (!isedit) {
$('#txtTitle').val('');
$('#txtBeschr').val('');
}
$('#myModal').modal('hide');
let ret = await putToDbase({cmd: 'PUT_CALENTRY', data: entry});
return ret;
}
// ********************
// Ende Button-Event-Handler
// ********************
// Die Startzeit des dritten (der liegt gesichert im aktuellen Monat)
// Regelführungstermins des Monats finden
// Params:
// keine
// Return:
// entweder die Startzeit, falls gefunden oder aber 20 [Uhr]
function getFuehrungszeitZeit() {
if (fuehrungszeiten[3].start !== undefined) {
let st = moment(fuehrungszeiten[3].start);
return st.hour();
} else {
return 20;
}
}
// das Dialog-Fenster mit den Datums/Zeit-Optionen füllen
// Params:
// st_seldate gewähltes Startdatum
// end_seldate gewähltes Enddatum (oder leer => null)
// Return:
// nix
function fillModal(st_seldate, end_seldate) {
let start, end;
if (isedit) {
start = moment(st_seldate);
end = moment(end_seldate);
} else {
start = moment(st_seldate);
start.hour(getFuehrungszeitZeit());
end = moment(st_seldate);
end.hour(getFuehrungszeitZeit());
end = end.add(default_dauer, 'm');
$('#txtTitle').val('');
$('#txtBeschr').val('');
}
$('#st_date_day #end_date_day').html('');
$('#end_date_day').html('');
for (let i = 1; i <= 31; i++) {
$('#st_date_day').append(`<option ${start.date() == i ? 'selected' : ''}>${i}</option>`);
$('#end_date_day').append(`<option ${end.date() == i ? 'selected' : ''}>${i}</option>`);
}
$('#st_date_month #end_date_month').html('');
$('#end_date_month').html('');
for (let i = 1; i <= 12; i++) {
$('#st_date_month').append(`<option value=${i} ${(start.month() + 1) == i ? 'selected' : ''}>${months_short[i]}</option>`);
$('#end_date_month').append(`<option value=${i} ${(end.month() + 1) == i ? 'selected' : ''}>${months_short[i]}</option>`);
}
$('#st_date_year #end_date_year').html('');
$('#end_date_year').html('');
for (let i = 2020; i <= 2030; i++) {
$('#st_date_year').append(`<option ${start.year() == i ? 'selected' : ''}>${i}</option>`);
$('#end_date_year').append(`<option ${end.year() == i ? 'selected' : ''}>${i}</option>`);
}
$('#st_date_hours').html('');
$('#end_date_hours').html('');
for (let i = 0; i <= 23; i++) {
$('#st_date_hours').append(`<option ${start.hour() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#end_date_hours').append(`<option ${end.hour() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
}
$('#st_date_minutes').html('');
$('#end_date_minutes').html('');
for (let i = 0; i <= 50; i += 10) {
$('#st_date_minutes').append(`<option ${start.minute() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
$('#end_date_minutes').append(`<option ${end.minute() == i ? 'selected' : ''}>${("0" + i).slice(-2)}</option>`);
}
}
// Aus der BEOs-Datenbank die namen und die Kürzel der
// für den übergebenen Führungstag aktiven BEOs
// Params:
// beos Array der Daten der BEOs
// data Objekt mit dem Führungstag
// Return:
// Objekt mit den Kürzeln und den Namen der aktiven BEOs
function checkGroupinBeos(beos, data) {
let kurz = "";
let name = "";
for (let b of beos) {
let grps = b.gruppe.split(',');
for (let i in grps) {
if (data.title == grps[i]) {
kurz += b.kürzel + ',';
name += `${b.vorname} ${b.name}<br />`;
}
}
}
return {k: kurz.slice(0, -1), n: name};
}
// Aus der Datenbank die Regel-Führungstage des Monat holen und die
// aktiven BEOs dazu eintragen
// Params:
// start Startdatum zu Suche in der DB
// end Endedatum dazu
// beos Array mit den BEOs daten
// Retur:
// Array mit den Regel-Führungszeiten des Monats mit den BEOs
async function getFdates(start, end, beos) {
// Zuerst die regelmäßigen Führungen hole
fuehrungszeiten = await fetchFromDbase({cmd: 'GET_FDATES', start: start, end: end});
for (let i = 0; i < fuehrungszeiten.length; i++) {
let start = moment(fuehrungszeiten[i].start);
start.add(fuehrungszeiten[i].uhr,'h')
fuehrungszeiten[i].start = moment(start).format()
let kurz = checkGroupinBeos(beos, fuehrungszeiten[i]);
if (kurz.k != "") {
fuehrungszeiten[i].title += " " + kurz.k;
fuehrungszeiten[i].namen = kurz.n;
}
}
return fuehrungszeiten;
}
// Aus der Datenbank die eingetragenen Führungen für den laufenden Monat holen
// Params:
// start, ende Start- und Endezeitpunk für die Suche in der DB
// Return:
// Array mit Führungen des laufenden Monats
async function getCaldates(start, end) {
return await fetchFromDbase({cmd: 'GET_CALENTRIES', start: start, end: end});
}
// Prüfen, ob das übergeben Datum in der Feiertags-Liste enthalten ist
// Params:
// date das zu prüfende Datum
//
// Return:
// true: das Datum ist ein Feiertag
// false: es ist keiner
async function checkInHoliday(date) {
const feiertage = await getHolidays(date)
let month = (date.getMonth()+1) <= 9 ? "0" + (date.getMonth()+1) : (date.getMonth()+1)
let day = date.getDate() <= 9 ? "0"+date.getDate() : date.getDate()
const datum = date.getFullYear() + '-' + month + "-" + day;
return feiertage.findIndex(x => x.start == datum);
}
// Die Feiertage in Baden-Württemberg von einer API via HTTP abholen
// Params:
// start, end Jahre, für das die Feiertage geholt werden
// ACHTUNG: es werden max. 2 Jahre geholt (start und end), da der
// Kalender max. 2 folgende Jahre anfordert.
// Return:
// Array mit den Feiertagen
async function getHolidays(start, end) {
let data = [];
let startyear = moment(start).year();
let endyear = moment(end).year();
let response = await fetch('https://feiertage-api.de/api/?jahr=' + startyear + '&nur_land=BW');
let holidays = await response.json();
for (const [k, v] of Object.entries(holidays)) {
if(k == "Ostermontag") {
data.push({start: moment(v.datum).subtract(1,'days').format("YYYY-MM-DD"), title: "Ostersonntag"});
}
if(k == "Pfingstmontag") {
data.push({start: moment(v.datum).subtract(1,'days').format("YYYY-MM-DD"), title: "Pfingstsonntag"});
}
data.push({start: v.datum, title: k});
}
if (startyear != endyear) {
let response = await fetch('https://feiertage-api.de/api/?jahr=' + endyear + '&nur_land=BW');
let holidays = await response.json();
for (const [k, v] of Object.entries(holidays)) {
data.push({start: v.datum, title: k});
}
}
return data;
}
// die ersten Sonntage des Monates im laufenden Jahr finden
// Params:
// year: das Jahr, für das die Sonntage gesucht werden
// Return:
// Array mit den Sonntagen
function getSundays(dt) {
let year = moment(dt).year();
const cm = moment(dt).month()
if (moment(dt).month() == 11) {
year += 1;
}
let data = [];
for (let i = 1; i <= 12; i++) {
let d = moment(year + '-' + i + '-01');
let w = d.isoWeekday();
let dt = ''
if (w == 7) {
dt = d.format('YYYY-MM-DD');
} else {
dt = d.add(7 - w, 'd').format('YYYY-MM-DD')
}
data.push({start: dt + 'T11:00:00', uhr: 11, title: 'Sonnenführung' })
}
return data;
}
// Anzeige des Dialogfensters zum Ändern/Löschen einer Führung
//
// Dieses Dialogfenster wird nur aufgerufen, wenn auf eine händisch
// eingetragene Führung geklickt wird (nicht bei Regeleführungen).
// Params:
// entryInfo Daten des geklickten Eintrages
// Return:
// nix
function showViewModal(entryInfo) {
let str
$('#viewModal .modal-title').text(entryInfo.title);
if (entryInfo._def.extendedProps.description !== undefined) { // Führungs-Eintrag
str = getFuehrungsInfo(entryInfo);
$('#btn_delete').show()
$('#btn_edit').show()
} else {
str = getRegularInfo(entryInfo)
$('#btn_delete').hide()
$('#btn_edit').hide()
}
$('#viewModal .viewdesc').html(str);
$('#viewModal').modal();
}
// Informationen einer Regelführung für das Popup zusammenbauen
function getRegularInfo(entryInfo) {
let curDate = moment(entryInfo.start).format('YYYYMMDD');
let besucher = entryInfo._def.extendedProps.count;
if (besucher == null) { besucher = "0"}
besucher = parseInt(besucher)
let wtg = moment(entryInfo.start).isoWeekday();
let nbr = entryInfo.title.split(' ');
let str = `<h6>${weekdays[wtg - 1]}s-Team ${nbr[1]}</h6>`;
str += `<div id="beonamen">${entryInfo._def.extendedProps.namen}</div>`;
let btxt = besucher != 0 ? `<strong>${besucher}</strong> angemeldete Besucher` : 'Keine Anmeldungen';
str += btxt;
str += `<br />Von ${moment(entryInfo.start).format("HH:mm")} Uhr bis: ${moment(entryInfo.start).add(default_dauer, 'm').format("HH:mm")} Uhr`;
return str;
}
// Informationen für eine händisch eingetragene Führung zusammenbauen.
// Wird bei dem Popup und bei dem Ändern/löschen verwendent
function getFuehrungsInfo(entryInfo) {
let str = entryInfo._def.extendedProps.description;
str += `<br /><br />Start: ${moment(entryInfo.start).format("YYYY-MM-DD HH:mm")} Uhr <br />Ende: ${moment(entryInfo.end).format("YYYY-MM-DD HH:mm")} Uhr`;
return str;
}
function showAnleitung() {
console.log("Anleitung geklicked");
$('.viewAnleitung').load("docs/Anleitung.html",function() {
$('#infoModal').modal({show:true, focus:true});
});
};
}); // Ende document.addEventListener()

View File

@@ -0,0 +1,46 @@
// VersiosNummern und -Geschichte
var VERSION="1.3.2";
var VDATE="2024-09-20";
/* History
Rev. Datum Entwickler
1.3.2 2024-09-20 rxf
- Sonnenführunge mit anzeigen
1.3.1 2022-11-28 rxf
- Problem mit der Anzahl der Besucher behoben
1.3.0 2022-08-24 rxf
- semantic version numbering
- Kalender auf Premium Version -> bessere prints
- Kalender: kein 'hoover' mehr
1.24 2021-09.21 rxf
- Hover bei Feiertagen gibt kein PopUp mehr
1.23 2021-08-22 rxf
- Zugriff auf Gruppe über 'fdatum'
1.22 2021-07-25 rxf
- Anzahl der angemeldeten Besucher anzeigen
1.21 2020-11-16 rxf
- Hintergund etwas heller
- Oster/Pfingst-Sonntag dazu
1.20 2020-11-16 rxf
- Anleitung dazu
1.10 2020-11-13 rxf
- Ende-zeiten mit eingeführt
1.01 2020-11-12 rxf
- zum Testen auf test.sternwarte-welzheim.de ausgeliefert
1.0 2020-11-10 rxf
- Erste fertige Version.
*/