www/content/page/calendar.md

370 lines
12 KiB
Markdown
Raw Normal View History

2025-02-26 19:47:07 +01:00
---
title: "Kalender"
subtitle: "Der Kalender des CCCB"
date: 2025-02-26T10:00:00+02:00
menu:
main:
parent: "verein"
---
<!-- Kalender-Widget -->
<style>
.calendar-container {
display: flex;
flex-wrap: wrap;
width: 100%;
gap: 20px;
}
#calendar {
flex: 1;
min-width: 300px;
}
#event-panel {
flex: 1;
min-width: 300px;
background-color: var(--color-bg-secondary);
padding: 15px;
border-radius: 5px;
min-height: 300px;
display: none;
}
#calendar-controls {
text-align: center;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
#calendar-controls button {
background: none;
border: none;
font-size: 2em;
cursor: pointer;
padding: 5px 15px;
}
#calendar-table {
width: 100%;
border-collapse: collapse;
}
#calendar-table th, #calendar-table td {
border: 1px solid var(--color-border);
padding: 5px;
text-align: center;
vertical-align: top;
height: 60px;
width: 14.28%;
}
#calendar-table th {
background-color: var(--color-bg-secondary);
}
#calendar-table td {
background-color: var(--color-bg-primary);
position: relative;
cursor: pointer;
}
#calendar-table td:hover {
background-color: var(--color-bg-hover);
}
.event-dot {
height: 8px;
width: 8px;
background-color: greenyellow;
border-radius: 50%;
display: block;
margin: 5px auto 0;
}
.has-event {
background-color: var(--color-bg-secondary) !important;
}
.selected-day {
background-color: var(--color-bg-hover) !important;
font-weight: bold;
}
#event-date {
font-size: 1.2em;
margin-bottom: 15px;
}
.event-item {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid var(--color-border);
}
.event-title {
font-weight: bold;
margin-bottom: 5px;
}
.event-time, .event-description {
font-size: 0.9em;
margin-bottom: 5px;
}
.no-events {
font-style: italic;
color: var(--color-text-secondary);
}
</style>
<div class="calendar-container">
<div id="calendar">
<div id="calendar-controls">
<button id="prev-month">&larr;</button>
<span id="current-month"></span>
<button id="next-month">&rarr;</button>
</div>
<table id="calendar-table">
<thead>
<tr>
<th>Mo</th>
<th>Di</th>
<th>Mi</th>
<th>Do</th>
<th>Fr</th>
<th>Sa</th>
<th>So</th>
</tr>
</thead>
<tbody id="calendar-body"></tbody>
</table>
</div>
<div id="event-panel">
<div id="event-date"></div>
<div id="event-details"></div>
</div>
</div>
<script>
(function(){
let events = [];
let eventsByDate = {};
// Funktion zum Parsen der ICS-Datei
function parseICS(icsText) {
let events = [];
let lines = icsText.split(/\r?\n/);
let event = null;
lines.forEach(line => {
if (line.startsWith("BEGIN:VEVENT")) {
event = {};
} else if (line.startsWith("END:VEVENT")) {
if (event) events.push(event);
event = null;
} else if (event) {
let colonIndex = line.indexOf(":");
if (colonIndex > -1) {
let key = line.substring(0, colonIndex);
let value = line.substring(colonIndex + 1);
if (key.startsWith("DTSTART")) {
event.start = value;
} else if (key.startsWith("DTEND")) {
event.end = value;
} else if (key.startsWith("SUMMARY")) {
event.summary = value;
} else if (key.startsWith("DESCRIPTION")) {
event.description = value;
}
}
}
});
return events;
}
// Hilfsfunktion: Parst einen ICS-Datum-String ins Format "YYYY-MM-DD"
function parseDateString(icsDateStr) {
// Erwartet entweder ganztägige Daten (YYYYMMDD) oder Datum+Zeit (YYYYMMDDTHHmmssZ)
if (icsDateStr.length === 8) {
let year = icsDateStr.substring(0, 4);
let month = icsDateStr.substring(4, 6);
let day = icsDateStr.substring(6, 8);
return `${year}-${month}-${day}`;
} else if (icsDateStr.length >= 15) {
let year = icsDateStr.substring(0, 4);
let month = icsDateStr.substring(4, 6);
let day = icsDateStr.substring(6, 8);
return `${year}-${month}-${day}`;
}
return null;
}
// Kalender initialisieren
let currentYear, currentMonth;
const currentMonthElem = document.getElementById("current-month");
const calendarBody = document.getElementById("calendar-body");
const eventPanel = document.getElementById("event-panel");
const eventDateElem = document.getElementById("event-date");
const eventDetailsElem = document.getElementById("event-details");
document.getElementById("prev-month").addEventListener("click", function(){
currentMonth--;
if (currentMonth < 0) {
currentMonth = 11;
currentYear--;
}
renderCalendar(currentYear, currentMonth);
});
document.getElementById("next-month").addEventListener("click", function(){
currentMonth++;
if (currentMonth > 11) {
currentMonth = 0;
currentYear++;
}
renderCalendar(currentYear, currentMonth);
});
function renderCalendar(year, month) {
// Setze die Monatsbeschriftung (in Deutsch)
const monthNames = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
currentMonthElem.textContent = monthNames[month] + " " + year;
calendarBody.innerHTML = "";
let firstDay = new Date(year, month, 1);
let firstDayIndex = (firstDay.getDay() + 6) % 7; // Montag = 0, Dienstag = 1, etc.
let daysInMonth = new Date(year, month + 1, 0).getDate();
let row = document.createElement("tr");
// Leere Zellen vor dem 1. Tag
for (let i = 0; i < firstDayIndex; i++){
let cell = document.createElement("td");
row.appendChild(cell);
}
// Tage hinzufügen
for (let day = 1; day <= daysInMonth; day++){
if (row.children.length === 7) {
calendarBody.appendChild(row);
row = document.createElement("tr");
}
let cell = document.createElement("td");
cell.innerHTML = "<strong>" + day + "</strong>";
let dayStr = day < 10 ? "0" + day : day;
let monthStr = (month + 1) < 10 ? "0" + (month + 1) : (month + 1);
let dateKey = year + "-" + monthStr + "-" + dayStr;
if (eventsByDate[dateKey]) {
let eventDot = document.createElement("div");
eventDot.className = "event-dot";
cell.appendChild(document.createElement("br"));
cell.appendChild(eventDot);
cell.dataset.dateKey = dateKey;
cell.addEventListener("click", function() {
// Clear previous selections
document.querySelectorAll('.selected-day').forEach(el => {
el.classList.remove('selected-day');
});
cell.classList.add('selected-day');
showEventDetails(dateKey);
});
}
row.appendChild(cell);
}
// Falls die letzte Zeile nicht komplett ist
while (row.children.length < 7) {
let cell = document.createElement("td");
row.appendChild(cell);
}
calendarBody.appendChild(row);
}
function createEventLink(eventTitle) {
if (eventTitle.startsWith("Datengarten")) {
// Extract the number after "Datengarten "
const match = eventTitle.match(/Datengarten\s+(\d+)/i);
if (match && match[1]) {
return `https://berlin.ccc.de/datengarten/${match[1]}/`;
}
}
// For other titles, convert to lowercase and use as path
const slug = eventTitle.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '');
return `https://berlin.ccc.de/page/${slug}/`;
}
function showEventDetails(dateKey) {
const events = eventsByDate[dateKey];
eventDateElem.textContent = formatDate(dateKey);
eventDetailsElem.innerHTML = "";
if (events && events.length > 0) {
events.forEach(ev => {
let eventItem = document.createElement("div");
eventItem.className = "event-item";
let eventTitle = document.createElement("div");
eventTitle.className = "event-title";
// Create a link for the event title
let titleLink = document.createElement("a");
titleLink.textContent = ev.summary;
titleLink.href = createEventLink(ev.summary);
titleLink.target = "_blank";
eventTitle.appendChild(titleLink);
eventItem.appendChild(eventTitle);
let eventTime = document.createElement("div");
eventTime.className = "event-time";
eventTime.textContent = `Start: ${formatTime(ev.start)}, End: ${formatTime(ev.end)}`;
eventItem.appendChild(eventTime);
if (ev.description) {
let eventDescription = document.createElement("div");
eventDescription.className = "event-description";
eventDescription.textContent = ev.description;
eventItem.appendChild(eventDescription);
}
eventDetailsElem.appendChild(eventItem);
});
} else {
let noEvents = document.createElement("div");
noEvents.className = "no-events";
noEvents.textContent = "Keine Veranstaltungen an diesem Tag.";
eventDetailsElem.appendChild(noEvents);
}
eventPanel.style.display = "block";
}
function formatDate(dateStr) {
// Convert YYYY-MM-DD to DD.MM.YYYY
const parts = dateStr.split("-");
return `${parts[2]}.${parts[1]}.${parts[0]}`;
}
function formatTime(icsTimeStr) {
// Format time for display
if (icsTimeStr.length === 8) {
// All-day event
return "Ganztägig";
} else if (icsTimeStr.length >= 15) {
// Time-specific event
const hour = icsTimeStr.substring(9, 11);
const minute = icsTimeStr.substring(11, 13);
return `${hour}:${minute}`;
}
return icsTimeStr;
}
// ICS-Datei abrufen und Events verarbeiten
fetch('/all.ics')
.then(response => response.text())
.then(data => {
events = parseICS(data);
events.forEach(ev => {
let dateKey = parseDateString(ev.start);
if (dateKey) {
if (!eventsByDate[dateKey]) {
eventsByDate[dateKey] = [];
}
eventsByDate[dateKey].push(ev);
}
});
let today = new Date();
currentYear = today.getFullYear();
currentMonth = today.getMonth();
renderCalendar(currentYear, currentMonth);
})
.catch(err => console.error('Fehler beim Laden der ICS-Datei:', err));
})();
</script>