switch to ics files; make calendars work; fix some minor issues

This commit is contained in:
XenGi 2026-08-12 02:01:58 +02:00
commit c28f04c6e8
Signed by: xengi
SSH key fingerprint: SHA256:jxWM2RTHvxxcncXycwwWkP7HCWb4VREN05UGJTbIPZg
19 changed files with 318 additions and 259 deletions

View file

@ -19,10 +19,10 @@ document.addEventListener('DOMContentLoaded', function() {
if (colonIndex > -1) {
let key = line.substring(0, colonIndex);
let value = line.substring(colonIndex + 1);
// Handle properties with parameters (like TZID)
const baseKey = key.split(";")[0];
if (baseKey === "DTSTART") {
event.start = value;
event.startParams = key.includes(";") ? key.substring(key.indexOf(";") + 1) : null;
@ -46,14 +46,14 @@ document.addEventListener('DOMContentLoaded', function() {
function parseDateString(icsDateStr) {
// Handle different date formats
if (!icsDateStr) return null;
// For basic date format: YYYYMMDD
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}`;
}
}
// For datetime formats: YYYYMMDDTHHmmssZ or YYYYMMDDTHHmmss
else if (icsDateStr.includes("T")) {
let year = icsDateStr.substring(0, 4);
@ -67,34 +67,34 @@ document.addEventListener('DOMContentLoaded', function() {
// Extract date components from different date formats
function getDateComponents(icsDateStr) {
if (!icsDateStr) return null;
// Basic handling - extract YYYY, MM, DD regardless of format
const year = parseInt(icsDateStr.substring(0, 4));
const month = parseInt(icsDateStr.substring(4, 6)) - 1; // 0-based months
const day = parseInt(icsDateStr.substring(6, 8));
return { year, month, day };
}
function expandRecurringEvents(event, year, month) {
if (!event.rrule) return [event];
const rruleStr = event.rrule;
// Get start date components
const startComponents = getDateComponents(event.start);
if (!startComponents) return [event];
const startDate = new Date(
startComponents.year,
startComponents.month,
startComponents.day
);
const rangeStart = new Date(year, month, 1);
const rangeEnd = new Date(year, month + 1, 0);
const expandedEvents = [];
if (rruleStr.includes("FREQ=WEEKLY") && rruleStr.includes("BYDAY")) {
const bydayMatch = rruleStr.match(/BYDAY=([^;]+)/);
if (bydayMatch) {
@ -103,7 +103,7 @@ document.addEventListener('DOMContentLoaded', function() {
'MO': 1, 'TU': 2, 'WE': 3, 'TH': 4, 'FR': 5, 'SA': 6, 'SU': 0
};
const targetDay = dayMap[dayCode];
if (targetDay !== undefined) {
// Create events for each matching day in the month
let day = 1;
@ -112,13 +112,13 @@ document.addEventListener('DOMContentLoaded', function() {
if (testDate.getDay() === targetDay && testDate >= startDate) {
const newEvent = {...event};
const eventDate = formatDateForICS(testDate);
// Preserve time portion from original event
const timePart = event.start.includes('T') ?
const timePart = event.start.includes('T') ?
event.start.substring(event.start.indexOf('T')) : '';
const endTimePart = event.end.includes('T') ?
const endTimePart = event.end.includes('T') ?
event.end.substring(event.end.indexOf('T')) : '';
newEvent.start = eventDate + timePart;
newEvent.end = eventDate + endTimePart;
expandedEvents.push(newEvent);
@ -141,15 +141,15 @@ document.addEventListener('DOMContentLoaded', function() {
const dayMap = {
'MO': 1, 'TU': 2, 'WE': 3, 'TH': 4, 'FR': 5, 'SA': 6, 'SU': 0
};
bydays.forEach(byday => {
const occurrence = parseInt(byday) || 1;
const dayCode = byday.slice(-2);
const dayIndex = dayMap[dayCode];
let day = 1;
let count = 0;
while (day <= rangeEnd.getDate()) {
const testDate = new Date(year, month, day);
if (testDate.getDay() === dayIndex) {
@ -157,13 +157,13 @@ document.addEventListener('DOMContentLoaded', function() {
if (count === occurrence || (occurrence < 0 && day > rangeEnd.getDate() + occurrence * 7)) {
const newEvent = {...event};
const eventDate = new Date(year, month, day);
// Preserve time portion from original event
const timePart = event.start.includes('T') ?
const timePart = event.start.includes('T') ?
event.start.substring(event.start.indexOf('T')) : '';
const endTimePart = event.end.includes('T') ?
const endTimePart = event.end.includes('T') ?
event.end.substring(event.end.indexOf('T')) : '';
newEvent.start = formatDateForICS(eventDate) + timePart;
newEvent.end = formatDateForICS(eventDate) + endTimePart;
expandedEvents.push(newEvent);
@ -174,7 +174,7 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
}
return expandedEvents.length > 0 ? expandedEvents : [event];
}
@ -206,7 +206,7 @@ document.addEventListener('DOMContentLoaded', function() {
function updateEventsForMonth(year, month) {
// Clear existing events for this month view
eventsByDate = {};
// Process each event, expanding recurring ones
events.forEach(ev => {
if (ev.rrule) {
@ -228,7 +228,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Check if this event belongs to current month view
const eventYear = parseInt(dateKey.split('-')[0]);
const eventMonth = parseInt(dateKey.split('-')[1]) - 1;
if (eventYear === year && eventMonth === month) {
if (!eventsByDate[dateKey]) {
eventsByDate[dateKey] = [];
@ -238,7 +238,7 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
});
renderCalendar(year, month);
}
@ -267,16 +267,16 @@ document.addEventListener('DOMContentLoaded', function() {
}
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 dotsContainer = document.createElement("div");
dotsContainer.className = "event-dots-container";
cell.classList.add("has-event");
// Gruppe Events nach Typ
const events = eventsByDate[dateKey];
const hasMembersOnly = events.some(e => e.summary.toLowerCase().includes("members only"));
@ -285,12 +285,12 @@ document.addEventListener('DOMContentLoaded', function() {
const hasSpieleabend = events.some(e => e.summary.toLowerCase().includes("spieleabend"));
const hasRegular = events.some(e => {
const title = e.summary.toLowerCase();
return !title.includes("members only") &&
!title.includes("subbotnik") &&
!title.includes("bastelabend") &&
return !title.includes("members only") &&
!title.includes("subbotnik") &&
!title.includes("bastelabend") &&
!title.includes("spieleabend");
});
// Füge Dots entsprechend der Event-Typen hinzu
if (hasMembersOnly) {
let dot = document.createElement("div");
@ -307,7 +307,7 @@ document.addEventListener('DOMContentLoaded', function() {
dot.className = "event-dot event-dot-greenyellow";
dotsContainer.appendChild(dot);
}
cell.appendChild(dotsContainer);
cell.dataset.dateKey = dateKey;
cell.addEventListener("click", function() {
@ -336,8 +336,8 @@ document.addEventListener('DOMContentLoaded', function() {
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}/`;
@ -355,13 +355,13 @@ document.addEventListener('DOMContentLoaded', function() {
let eventTitle = document.createElement("div");
eventTitle.className = "event-title";
// Create a link for the event title
let titleLink = document.createElement("h");
titleLink.textContent = ev.summary;
titleLink.target = "_blank";
eventTitle.appendChild(titleLink);
eventItem.appendChild(eventTitle);
let eventTime = document.createElement("div");
@ -372,7 +372,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (ev.description) {
let eventDescription = document.createElement("div");
eventDescription.className = "event-description";
// Check if the description is a URL and make it a clickable link
if (ev.description.trim().startsWith('http')) {
let linkElement = document.createElement("a");
@ -384,7 +384,7 @@ document.addEventListener('DOMContentLoaded', function() {
} else {
eventDescription.textContent = ev.description;
}
eventItem.appendChild(eventDescription);
}
@ -409,7 +409,7 @@ document.addEventListener('DOMContentLoaded', function() {
function formatTime(icsTimeStr) {
// Format time for display
if (!icsTimeStr) return "";
if (icsTimeStr.length === 8) {
// All-day event
return "Ganztägig";
@ -431,20 +431,18 @@ document.addEventListener('DOMContentLoaded', function() {
}
// ICS-Datei abrufen und Events verarbeiten
fetch('/all.ics')
fetch('/calendars/all.ics')
.then(response => response.text())
.then(data => {
events = parseICS(data);
// Initialize with current date
let today = new Date();
currentYear = today.getFullYear();
currentMonth = today.getMonth();
// Process events for current month
updateEventsForMonth(currentYear, currentMonth);
updateEventsForMonth(currentYear, currentMonth);
})
.catch(err => console.error('Fehler beim Laden der ICS-Datei:', err));
})();