First Commit
This commit is contained in:
546
html/sternwarte/kalender/js/kalender.js
Normal file
546
html/sternwarte/kalender/js/kalender.js
Normal 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()
|
||||
|
||||
46
html/sternwarte/kalender/js/version.js
Normal file
46
html/sternwarte/kalender/js/version.js
Normal 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.
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user